perm filename INTERP.PAS[AL,HE]2 blob
sn#680856 filedate 1982-10-15 generic text, type C, neo UTF8
COMMENT ⊗ VALID 00055 PAGES
C REC PAGE DESCRIPTION
C00001 00001
C00006 00002 (*$E+ Routines to interpret an AL program *)
C00010 00003 (* datatype definitions *)
C00013 00004 (* statement definitions *)
C00017 00005 (* auxiliary definitions: variable, etc. *)
C00019 00006 (* definition of the ubiquitous NODE record *)
C00025 00007 (* records for parser: ident, token, resword *)
C00028 00008 (* process descriptor blocks & environment record definitions *)
C00032 00009 (* definition of AL-ARM messages *)
C00034 00010 (* global variables *)
C00036 00011 (* external routines *)
C00043 00012 (* message passing routines: sendCmd, sendTrans, getReply, whereArm *)
C00047 00013 (* aux routines: push, pop, upTrans, getELev, getEntry, getVar, gtVarn, getVal, setVal, getNval *)
C00056 00014 (* aux routines: getPdb, freePdb, getEvent, freeEvent *)
C00059 00015 (* graph structure routines: nextTime, getFrame, getDevice, feval, eval, change, invalidate, stvals ... *)
C00071 00016 (* aux routines to create & destroy variables: enterEntry,makeCmon,makeVar,killVar,killEnv,killNode,killStack *)
C00083 00017 (* aux io routines: prntSval, prntVec, prntTrans, prntStrng, prntPlist, onum, prntVar, badjoints *)
C00088 00018 (* aux routines: addPdb, sleep, deClkQueue, msgDispatch, swap *)
C00099 00019 (* aux routines: initArms,initWorld,consDef,passConstants,flushLevel,flushAll,unwind,flushPdb,flushKids *)
C00113 00020 (* aux routines: cmonEnable, cmonDisable, cmonCheck *)
C00119 00021 (* expression evaluator: evalExp *)
C00137 00022 procedure doProg (* ** ** *)
C00138 00023 procedure doBlock
C00140 00024 procedure doCoblock
C00143 00025 procedure doEnd
C00147 00026 procedure doFor
C00150 00027 procedure doIf
C00151 00028 procedure doWhile
C00152 00029 procedure doUntil
C00153 00030 procedure doCase
C00155 00031 procedure doCall
C00156 00032 procedure doReturn
C00160 00033 procedure doPrint
C00161 00034 procedure doPrompt
C00163 00035 procedure doPause
C00164 00036 procedure doAbort
C00166 00037 procedure doAssign
C00168 00038 procedure doSignal
C00170 00039 procedure doWait
C00172 00040 procedure doEnable
C00173 00041 procedure doDisable
C00174 00042 (* affixment auxiliary routines: affixaux, unfixaux & unfix *)
C00180 00043 procedure doAffix
C00186 00044 procedure doUnfix
C00187 00045 (* aux routines for motions: forcebits, getMechbits, moveStart, moveEnd, moveRetry *)
C00202 00046 procedure doCmon
C00208 00047 procedure doMove
C00228 00048 procedure doOperate
C00232 00049 procedure doOpen (* & doClose *)
C00238 00050 procedure doCenter
C00240 00051 procedure doStop
C00242 00052 procedure doRetry
C00244 00053 procedure doSetbase
C00245 00054 procedure doWrist
C00247 00055 (* command loop *)
C00258 ENDMK
C⊗;
(*$E+ Routines to interpret an AL program *)
(*$S3000 use a large codesize *)
program interp;
(* The following bits are used during calls to the ARM servo *)
const
YARMDEV = 1; (* device numbers *)
YHANDDEV = 2;
BARMDEV = 3;
BHANDDEV = 4;
VISEDEV = 5;
DRIVERDEV = 6;
GARMDEV = 7;
GHANDDEV = 8;
RARMDEV = 9;
RHANDDEV = 10;
YELARM = 1B; (* Yellow arm *)
BLUARM = 4B; (* Blue arm *)
FTABLE = 400B; (* Force trans (C) in table coordinates *)
FHAND = 0B; (* " " " " hand coordinate system *)
XFORCE = 0B; (* Force along X direction of C *)
YFORCE = 1000B; (* " " Y " " " *)
ZFORCE = 2000B; (* " " Z " " " *)
XMOMENT = 3000B; (* Moment about X direction of C *)
YMOMENT = 4000B; (* " " Y " " " *)
ZMOMENT = 5000B; (* " " Z " " " *)
FSTOP = 10000B; (* In addition to starting cmon, stop arm *)
SIGMAG = 20000B; (* Test only magnitude of forces *)
SIGGE = 100000B; (* Start cmon if force ≥ specified value *)
SIGLT = 0B; (* " " " " < " " *)
BARMPOWER = 1B; (* bit defs - used in response to initarmscmd *)
YARMPOWER = 2B;
GARMPOWER = 4B;
RARMPOWER = 10B;
GARMCAL = 400B;
RARMCAL = 1000B;
NULLINGCB = 1B; (* control bits for trajectory specs *)
WOBBLECB = 2B;
DURLBCB = 20B; (* Duration: lower, upper & exact bounds *)
DURUBCB = 40B;
DUREQCB = 60B;
VELOCCB = 100B;
CODECB = 200B;
VIAPTCB = 400B;
DEPRPTCB = 1000B;
APPRPTCB = 2000B;
DESTPTCB = 10000B;
type
(* random type declarations for OMSI/SAIL compatibility *)
(* ascii = char; *)
atext = packed file of ascii;
(* atext = text; *)
(* Here are all the pointer-type definitions. Since the various *)
(* records reference each other so much, we have to put them all here. *)
vectorp = ↑vector;
transp = ↑trans;
strngp = ↑strng;
eventp = ↑event;
framep = ↑frame;
statementp = ↑statement;
varidefp = ↑varidef;
nodep = ↑node;
identp = ↑ident;
tokenp = ↑token;
reswordp = ↑resword;
pdbp = ↑pdb;
envheaderp = ↑envheader;
enventryp = ↑enventry;
environp = ↑environment;
cmoncbp = ↑cmoncb;
messagep = ↑message;
(* datatype definitions *)
datatypes = (pconstype, varitype, svaltype, vectype, rottype, transtype,
frametype, eventtype, strngtype, labeltype, proctype, arraytype,
reftype, valtype, cmontype, nulltype, undeftype,
dimensiontype, mactype, macargtype, freevartype);
scalar = real;
vector = record refcnt: integer; val: array [1..3] of real end;
trans = record refcnt: integer; val: array [1..3,1..4] of real end;
cstring = packed array [0..9] of ascii;
c4str = packed array [0..3] of ascii;
c5str = packed array [0..4] of ascii;
c20str = packed array [0..19] of ascii;
strng = record
next: strngp;
ch: cstring;
end;
event = record
next: eventp; (* all events are on one big list *)
count: integer;
waitlist: pdbp;
end;
frame = record
vari: varidefp; (* back pointer to variable name & info *)
calcs: nodep; (* affixment info *)
case ftype: boolean of (* frame = true, device = false *)
true: (valid: integer; val, fdepr: transp; dcntr: integer; dev: framep);
false: (mech: integer; case sdev: boolean of
true: (sdest: real); false: (tdest,appr,depr: transp));
(* sdev = true for scalar devices, false for frames *)
end;
byte = 0..255; (* doesn't really belong here, but... *)
(* statement definitions *)
stmntypes = (progtype, blocktype, coblocktype, endtype, coendtype,
fortype, iftype, whiletype, untiltype, casetype,
calltype, returntype,
printtype, prompttype, pausetype, aborttype, assigntype,
signaltype, waittype, enabletype, disabletype, cmtype,
affixtype, unfixtype,
movetype, operatetype, opentype, closetype, centertype,
stoptype, retrytype,
requiretype, definetype, macrotype, commenttype, dimdeftype,
setbasetype, wristtype, tovaltype, declaretype, emptytype);
(* more??? *)
statement = packed record
next, last: statementp; (* ↑ to lexical tokens? *)
stlab: varidefp;
exprs: nodep; (* any expressions used by this statement *)
nlines: integer;
bpt: boolean;
case stype: stmntypes of
progtype: (pcode: statementp; errors: integer);
blocktype,
declaretype,
endtype,
coendtype: (bcode, bparent: statementp; blkid: identp;
level, numvars: 0..255; variables: varidefp);
coblocktype: (threads: nodep; nthreads: integer; cblkid: identp);
fortype: (forvar, initial, step, final: nodep; fbody: statementp);
whiletype,
untiltype: (cond: nodep; body: statementp);
casetype: (index: nodep; range, ncases: integer; caselist: nodep);
iftype: (icond: nodep; thn, els: statementp);
pausetype: (ptime: nodep);
prompttype,
printtype,
aborttype: (plist: nodep; debugLev: integer);
returntype: (retval, rproc: nodep);
calltype,
assigntype: (what, aval: nodep);
affixtype,
unfixtype: (frame1, frame2, byvar, atexp: nodep; rigid: boolean);
signaltype,
waittype: (event: nodep);
movetype,
operatetype,
opentype,
closetype,
centertype,
stoptype: (cf, clauses: nodep);
retrytype: (rcode, rparent: statementp; olevel: integer);
cmtype: (oncond: nodep; conclusion: statementp;
deferCm, exprCm: boolean; cdef: varidefp);
enabletype,
disabletype: (cmonlab: varidefp);
requiretype: (rfil: boolean; rfils: strngp; rfilen: integer);
definetype: (macname,mpars: varidefp; macdef: tokenp);
commenttype: (len: integer; str: strngp; cbody: statementp);
dimdeftype: (dimname: varidefp; dimexpr: nodep);
setbasetype,
wristtype: (fvec, tvec: nodep);
tovaltype: (vstr: strngp; vlen: integer; waitp: boolean);
end;
(* auxiliary definitions: variable, etc. *)
varidef = packed record
next,dnext: varidefp;
name: identp;
level: 0..255; (* environment level *)
offset: 0..255; (* environment offset *)
dtype: varidefp; (* to hold the dimension info *)
tbits: 0..15; (* special type bits: array = 1, proc = 2, ref = 4 & ? *)
dbits: 0..15; (* for use by debugger/interpreter *)
case vtype: datatypes of
arraytype: (a: nodep);
proctype: (p: nodep);
labeltype,
cmontype: (s: statementp);
mactype: (mdef: statementp);
macargtype: (marg: tokenp);
pconstype: (c: nodep);
dimensiontype: (dim: nodep);
end;
(* definition of the ubiquitous NODE record *)
nodetypes = (exprnode, leafnode, listnode, clistnode, colistnode, forvalnode,
deprnode, viaptnode, apprnode, destnode, durnode,
sfacnode, wobblenode, swtnode, nullingnode, wristnode, cwnode,
arrivalnode, departingnode,
ffnode, forcenode, stiffnode, gathernode, cmonnode, errornode,
calcnode, arraydefnode, bnddefnode, bndvalnode,
waitlistnode, procdefnode, tlistnode, dimnode, commentnode);
exprtypes = ( svalop, (* scalar operators *)
sltop, sleop, seqop, sgeop, sgtop, sneop, (* relations *)
notop, orop, xorop, andop, eqvop, (* logical *)
saddop, ssubop, smulop, sdivop, snegop, sabsop, (* scalar ops *)
sexpop, maxop, minop, intop, idivop, modop,
sqrtop, logop, expop, timeop, (* functions *)
sinop, cosop, tanop, asinop, acosop, atan2op, (* trig *)
vdotop, vmagnop, tmagnop,
vecop, (* vector operators *)
vmakeop, unitvop, vaddop, vsubop, crossvop, vnegop,
svmulop, vsmulop, vsdivop, tvmulop, wrtop,
tposop, taxisop,
transop, (* trans operators *)
tmakeop, torientop, ttmulop, tvaddop, tvsubop, tinvrtop,
vsaxwrop, constrop, ftofop, deproachop, fmakeop, vmkfrcop,
ioop, (* i/o operators *)
queryop, inscalarop,
specop, (* special operators *)
arefop, callop, grinchop, macroop, vmop, adcop, dacop,
badop,
addop, subop, negop, mulop, divop, absop); (* for parsing *)
leaftypes = pconstype..strngtype;
reltypes = sltop..sgtop;
forcetypes = (force,absforce,torque,abstorque,angvelocity);
node = record
next: nodep;
case ntype: nodetypes of
exprnode: (op: exprtypes; arg1, arg2, arg3: nodep; elength: integer);
leafnode: (case ltype: leaftypes of
varitype: (vari: varidefp; vid: identp);
pconstype: (cname: varidefp; pcval: nodep);
svaltype: (s: scalar; wid: integer);
vectype: (v: vectorp);
transtype: (t: transp);
strngtype: (length: integer; str: strngp) ); (* also used by commentnodes *)
listnode: (lval: nodep);
clistnode: (cval: integer; stmnt: statementp; clast: nodep);
colistnode: (prev: nodep; cstmnt: statementp);
forvalnode: (fvar: enventryp; fstep: scalar);
arrivalnode:(evar: varidefp);
deprnode,
apprnode,
destnode: (loc: nodep; code: statementp);
viaptnode: (vlist: boolean; via,duration,velocity: nodep; vcode: statementp);
durnode: (durrel: reltypes; durval: nodep);
sfacnode,
wobblenode,
swtnode: (clval: nodep);
nullingnode,
wristnode,
cwnode: (notp: boolean); (* true = nonulling/zero wrist/counter_clockwise *)
ffnode: (ff: nodep; csys, pdef: boolean); (* true = world, false = hand *)
forcenode: (ftype: forcetypes; frel: reltypes; fval, fvec, fframe: nodep);
stiffnode: (fv, mv, coc: nodep);
gathernode: (gbits: integer);
cmonnode: (cmon: statementp; errhandlerp: boolean);
errornode: (eexpr: nodep);
calcnode: (rigid, frame1: boolean; other: framep; case tvarp: boolean of
false: (tval: transp); true: (tvar: enventryp) );
arraydefnode: (numdims: 1..10; bounds: nodep; combnds: boolean);
bnddefnode: (lower, upper: nodep);
bndvalnode: (lb, ub, mult: integer);
waitlistnode: (who: pdbp; when: integer);
procdefnode:(ptype: datatypes; level: 0..255;
pname, paramlist: varidefp; body: statementp);
tlistnode: (tok: tokenp);
dimnode: (time, distance, angle, dforce: integer);
end;
(* records for parser: ident, token, resword *)
ident = record
next: identp;
length: integer;
name: strngp;
curv: varidefp;
end;
tokentypes = (reswdtype, identtype, constype, comnttype, delimtype, labeldeftype,
macpartype);
constypes = svaltype..strngtype;
reswdtypes = (stmnttype, filtype, clsetype, decltype, optype);
filtypes = (abouttype,alongtype,attype,bytype,defertype,dotype,elsetype,
errmodestype,fromtype,handtype,intype,nonrigidlytype,rigidlytype,
sourcefiletype,steptype,thentype,totype,untltype,viatype,
withtype,worldtype,zeroedtype,oftype,wheretype,nowaittype);
clsetypes = (approachtype,arrivaltype,departuretype,departingtype,durationtype,
errortype,forcetype,forceframetype,forcewristtype,gathertype,
nonullingtype,nullingtype,stiffnesstype,torquetype,velocitytype,
wobbletype,cwtype,ccwtype,stopwaittimetype,angularvelocitytype);
token = record
next: tokenp;
case ttype: tokentypes of
constype: (cons: nodep);
comnttype: (len: integer; str: strngp);
delimtype: (ch: char);
reswdtype: (case rtype: reswdtypes of
stmnttype: (stmnt: stmntypes);
filtype: (filler: filtypes);
clsetype: (clause: clsetypes);
decltype: (decl: datatypes);
optype: (op: exprtypes) );
identtype: (id: identp);
labeldeftype: (lab: varidefp);
macpartype: (mpar: varidefp);
end;
resword = record
next: reswordp;
length: integer;
name: strngp;
case rtype: reswdtypes of
stmnttype: (stmnt: stmntypes);
filtype: (filler: filtypes);
clsetype: (clause: clsetypes);
decltype: (decl: datatypes);
optype: (op: exprtypes);
end;
(* process descriptor blocks & environment record definitions *)
queuetypes = (nullqueue,nowrunning,runqueue,inputqueue,eventqueue,sleepqueue,
forcewait,devicewait,joinwait,proccall);
pdb = packed record
nextpdb,next: pdbp; (* for list of all/active pdb's *)
level: 0..255; (* lexical level *)
mode: 0..255; (* expression/statement/sub-statement *)
priority: 0..255;
status: queuetypes; (* what are we doing *)
env: envheaderp;
spc: statementp; (* current statement *)
epc: nodep; (* current expression (if any) *)
sp: nodep; (* intermediate value stack *)
cm: cmoncbp; (* if we're a cmon point to our definition *)
mech: framep; (* current device being used *)
linenum: integer; (* used by editor/debugger *)
case procp: boolean of (* true if we're a procedure *)
true: (opdb: pdbp; (* pdb to restore when procedure exits *)
pdef: nodep); (* procedure definition node *)
false: (evt: eventp; (* event to signal when process goes away *)
sdef: statementp); (* first statement where process was defined *)
end;
envheader = packed record
parent: envheaderp;
env: array [0..4] of environp;
varcnt: 0..255; (* # of variables in use ??? *)
case procp: boolean of (* true if we're a procedure *)
true: (proc: nodep);
false:(block: statementp);
end;
enventry = record
case etype: datatypes of
svaltype: (s: scalar);
vectype: (v: vectorp);
transtype: (t: transp);
frametype: (f: framep);
eventtype: (evt: eventp);
strngtype: (length: integer; str: strngp);
cmontype: (c: cmoncbp);
proctype: (p: nodep; penv: envheaderp);
reftype: (r: enventryp);
arraytype: (a: envheaderp; bnds: nodep);
end;
environment = record
next: environp;
vals: array [0..9] of enventryp;
end;
cmoncb = record
running, enabled: boolean; (* cmon's status *)
cmon: statementp;
pdb: pdbp;
evt: eventp;
fbits: integer; (* bits for force sensing *)
oldcmon: cmoncbp; (* for debugger *)
end;
(* definition of AL-ARM messages *)
msgtypes = (initarmscmd,calibcmd,killarmscmd,wherecmd,
abortcmd,stopcmd,movehdrcmd,movesegcmd,
centercmd,operatecmd,movedonecmd,signalcmd,readjtcmd,drivecmd,
setccmd,forcesigcmd,forceoffcmd,biasoncmd,biasoffcmd,setstiffcmd,
zerowristcmd,wristcmd,gathercmd,getgathercmd,readadccmd,writedaccmd,
errorcmd,floatcmd);
errortypes = (noerror,noarmsol,timerr,durerr,toolong,useopr,nosuchdev,featna,
unkmess,srvdead,adcdead,nozind,exjtfc,paslim,nopower,badpot,devbusy,
baddev,timout,panicb);
message = record
cmd: msgtypes;
ok: boolean;
case integer of
1: (dev, bits, n: integer;
(* (dev, bits, n, evt: integer; (* for arm code version *)
evt: eventp;
dur: real;
case integer of
1: (v1,v2,v3: real);
2: (sfac,wobble,pos: real);
3: (val,angle,mag: real);
4: (max,min: real);
5: (error: errortypes));
2: (fv1,fv2,fv3,mv1,mv2,mv3: real); (* may never use these... *)
3: (t: array [1..6] of real);
end;
interr = record
case integer of
0: (i: integer);
1: (err,foo: errortypes);
end;
(* global variables *)
var curInt, activeInts, readQueue, allPdbs: pdbp;
sysEnv: envheaderp;
clkQueue: nodep;
allEvents: eventp;
resched, running, escapeI, singleThreadMode: boolean;
etime: integer; (* used by eval *)
curtime: integer; (* who knows where this will get updated - an ast? *)
stime: integer; (* used for clock queue on 10 *)
(* tty,ttyoutput: text; (* for terminal i/o *)
msg: messagep; (* for AL-ARM interaction *)
msgp: boolean; (* flag set if any messages pending *)
inputLine: array [1..20] of ascii;
inputp: integer; (* current offset into inputLine array above *)
inputReady: boolean;
debugLevel: integer;
(* various constant pointers *)
xhat,yhat,zhat,nilvect: vectorp;
niltrans: transp;
bpark, ypark, gpark, rpark: transp; (* arm park positions *)
(* various device & variable pointers *)
speedfactor: enventryp;
barm: framep;
(* external routines *)
procedure initAlloc; extern; (* from ALLOC.PAS *)
function newVector: vectorp; extern;
procedure relVector(v: vectorp); extern;
function newTrans: transp; extern;
procedure relTrans(t: transp); extern;
function newNode: nodep; extern;
procedure relNode(n: nodep); extern;
function newEvent: eventp; extern;
procedure relEvent(n: eventp); extern;
function newEentry: enventryp; extern;
procedure relEentry(n: enventryp); extern;
function newCmoncb: cmoncbp; extern;
procedure relCmoncb(n: cmoncbp); extern;
function newstrng: strngp; extern;
procedure relstrng(n: strngp); extern;
function newIdent: identp; extern;
procedure relIdent(n: identp); extern;
function newVaridef: varidefp; extern;
procedure relVaridef(n: varidefp); extern;
function newFrame: framep; extern;
procedure relFrame(n: framep); extern;
function newEheader: envheaderp; extern;
procedure relEheader(n: envheaderp); extern;
function newStatement: statementp; extern;
procedure relStatement(n: statementp); extern;
function newPdb: pdbp; extern;
procedure relPdb(n: pdbp); extern;
function newEnvironment: environp; extern;
procedure relEnvironment(n: environp); extern;
function sind(d: real): real; extern; (* from ARITH.PAS *)
function cosd(d: real): real; extern;
function tand(d: real): real; extern;
function asin(x: real): real; extern;
function acos(x: real): real; extern;
function atan2(x,y: real): real; extern;
function vdot (u,v: vectorp): scalar; extern;
function vmagn (v: vectorp): scalar; extern;
function vmake (a,b,c: scalar): vectorp; extern;
function svmul (s: scalar; v: vectorp): vectorp; extern;
function vsdiv (v: vectorp; s: scalar): vectorp; extern;
function vadd (u,v: vectorp): vectorp; extern;
function vsub (u,v: vectorp): vectorp; extern;
function unitv (v: vectorp): vectorp; extern;
function vcross (u,v: vectorp): vectorp; extern;
function tvmul (t: transp; v: vectorp): vectorp; extern;
function tpos (t: transp): vectorp; extern;
function torient (t: transp): transp; extern;
function taxis (t: transp): vectorp; extern;
function tmagn (t: transp): scalar; extern;
function tmake (t: transp; v: vectorp): transp; extern;
function tvadd (t: transp; v: vectorp): transp; extern;
function tvsub (t: transp; v: vectorp): transp; extern;
function ttmul (t1,t2: transp): transp; extern;
function tinvrt (t: transp): transp; extern;
function vsaxwr(ax: vectorp; w: real): transp; extern;
function construct(org,vx,vxy: vectorp): transp; extern;
function vmkfrc(v: vectorp): transp; extern;
function getsysVars: varidefp; extern; (* from PARSE.PAS *)
(* function startArm: boolean; extern; (* from RSXMSG.PAS *)
(* procedure initMsg(var buf: messagep; var flag: boolean); extern;
function SendArm: boolean; extern;
function GetArm: boolean; extern;
procedure signalArm; extern; *)
function startArm: boolean; begin startArm := true; end;
function sendArm: boolean; begin sendArm := true; end;
function getArm: boolean; begin getArm := true; end;
procedure ppLine; extern; (* from EDIT.PAS *)
procedure ppOutNow; extern;
procedure ppChar(ch: ascii); extern;
procedure pp5(ch: c5str; length: integer); extern;
procedure pp10(ch: cstring; length: integer); extern;
procedure pp10L(ch: cstring; length: integer);extern;
procedure pp20(ch: c20str; length: integer); extern;
procedure pp20L(ch: c20str; length: integer); extern;
procedure ppInt(i: integer); extern;
procedure ppReal(r: real); extern;
procedure ppStrng(length: integer; s: strngp); extern;
procedure ppDelChar; extern;
(* procedure freeStatement(s: statementp); extern; *) (* from FREE.PAS *)
function anyChar(var ch: ascii): boolean; extern; (* from DISP.FAI *)
procedure escInit(var flg: boolean); extern;
function getCurInt: pdbp; (* SAIL - for use by EDIT *)
begin
getCurInt := curInt;
end;
procedure setCurInt(p: pdbp);
begin
curInt := p;
end;
function getAllPdbs: pdbp;
begin
getAllPdbs := allPdbs;
end;
procedure setSingleThreadMode(b: boolean);
begin
singleThreadMode := b;
end;
(* message passing routines: sendCmd, sendTrans, getReply, whereArm *)
procedure sendCmd;
var b: boolean;
begin
b := sendArm; (* send message to ARM *)
(* with msg↑ do
if not ((cmd = movesegcmd) or (cmd = movehdrcmd) or
(cmd = setccmd) or (cmd = setstiffcmd)) then signalArm; (* tell arm *)
end;
procedure sendTrans(tr: transp);
var i,j,k: integer; b: boolean;
begin
b := sendArm; (* first send over message header *)
with msg↑,tr↑ do
begin
for k := 0 to 1 do
begin
for i := 1 to 3 do
for j := 1 to 2 do t[i + 3*(j-1)] := val[i,j + 2*k];
b := sendArm; (* send half over *)
end;
if refcnt <= 0 then relTrans(tr);
end;
end;
procedure msgDispatch; forward; (* handles signals & movedone from ARM *)
procedure getReply;
var ocmd: msgtypes; b: boolean;
begin
with msg↑ do
begin
ocmd := cmd; (* remember what we're waiting for *)
sendCmd; (* send request to ARM servo *)
repeat
b := getArm; (* try to read a message packet from ARM *)
if b and (cmd <> ocmd) then (* if we got one, was it our reply? *)
begin
msgDispatch; (* deal with whatever the ARM servo sent over *)
b := false; (* keep waiting for our reply *)
end
until b; (* wait for reply *)
end;
end;
function getEntry (level, offset: byte): enventryp; forward;
function whereArm (mech: integer): transp; (* to read in the arm's position *)
var tp: transp; i,j: integer; b: boolean;
ev: enventryp; (* for sim ver *)
begin
tp := newTrans;
with msg↑,tp↑ do
begin
cmd := wherecmd;
dev := mech;
getReply; (* go get 1st message packet *)
if ok then (* check there's no error *)
begin
for i := 1 to 3 do
for j := 1 to 2 do val[i,j] := t[i + 3*(j-1)]; (* copy result *)
repeat b := getArm until b; (* get 2nd packet (guaranteed to be next) *)
for i := 1 to 3 do
for j := 3 to 4 do val[i,j] := t[i + 3*(j-3)]; (* copy result *)
(* for simulation version *)
relTrans(tp);
case mech of (* get device offset *)
YARMDEV: i := 4;
BARMDEV: i := 0;
GARMDEV: i := 8;
RARMDEV: i := 12;
end;
ev := getEntry(0,i);
tp := ev↑.f↑.tdest; (* use wherever last move was to *)
end
else
begin (* *** ERROR - maybe we should complain??? *** *)
relTrans(tp); (* don't need this anymore *)
tp := niltrans;
end;
end;
whereArm := tp;
end;
(* aux routines: push, pop, upTrans, getELev, getEntry, getVar, gtVarn, getVal, setVal, getNval *)
procedure push (n: nodep);
begin (* no need to check for overflow *)
n↑.next := curInt↑.sp;
curInt↑.sp := n;
end;
function pop: nodep;
begin
pop := curInt↑.sp;
if curInt↑.sp = nil then
begin (* **** error - stack underflow **** *)
pp20L('Value Stack Underflo',20); ppChar('w'); ppLine;
(* code to show where error occurred & to maybe recover??? *)
end
else curInt↑.sp := curInt↑.sp↑.next;
end;
procedure upTrans (var t: transp; tp: transp);
begin
if tp <> nil then tp↑.refcnt := tp↑.refcnt + 1; (* indicate new trans is in use *)
if t <> nil then (* check for old value *)
begin
t↑.refcnt := t↑.refcnt - 1; (* we're done with trans now *)
if t↑.refcnt <= 0 then relTrans(t); (* release it if no one else wants it *)
end;
t := tp; (* copy new trans pointer *)
end;
function envlookup (offset: integer; envhdr: envheaderp): enventryp;
var i,j,k: integer; env: environp;
begin
i := offset div 10; (* which environment block *)
j := offset mod 10; (* entry in environment block *)
if i < 5 then env := envhdr↑.env[i] (* use direct look-up *)
else begin (* run through linked list *)
env := envhdr↑.env[4];
for k := 5 to i do env := env↑.next;
end;
envlookup := env↑.vals[j];
end;
function getELev(hdr: envheaderp): integer;
begin
if hdr = sysEnv then getELev := 0
else if hdr↑.procp then getELev := hdr↑.proc↑.level
else getELev := hdr↑.block↑.level;
end;
function getEntry (* (level, offset: byte): enventryp; *);
var hdr: envheaderp;
begin
if level = 0 then hdr := sysEnv (* level zero is predefined system variables *)
else
begin
hdr := curInt↑.env; (* look up the env entry given level-offset *)
while level < getELev(hdr) do hdr := hdr↑.parent; (* move up a level *)
if level <> getELev(hdr) then (* yow!!! no environment exists!!! *)
begin
pp20L('Attempt to access no',20); pp20('n-existent environme',20);
pp20('nt - good luck! ',16); ppLine;
end;
end;
getEntry := envlookup(offset,hdr);
end;
function getVar (level, offset: byte): enventryp;
var entry: enventryp; i, j: integer; p, b: nodep;
begin
entry := getEntry(level,offset); (* get the environment entry *)
while entry↑.etype = reftype do entry := entry↑.r; (* resolve indirect refs *)
if entry↑.etype = arraytype then (* do array reference *)
begin
b := entry↑.bnds;
j := 0;
repeat
p := pop; (* get this subscript's value *)
i := round(p↑.s);
relNode(p);
if i < b↑.lb then (* subscript error *)
begin
pp20L('Subscript index less',20); pp20(' than lower bound: ',19);
ppInt(i); ppLine;
i := b↑.lb
end
else if i > b↑.ub then (* subscript error *)
begin
pp20L('Subscript index grea',20); pp20('ter than lower bound',20);
pp5(': ',2); ppInt(i); ppLine;
i := b↑.ub
end;
j := j + b↑.mult * (i - b↑.lb);
b := b↑.next;
until b = nil;
entry := envlookup(j,entry↑.a); (* lookup the array entry *)
end;
getVar := entry;
end;
function gtVarn (n: nodep): enventryp;
begin
with n↑ do
if ntype = leafnode then
with vari↑ do gtVarn := getVar(level,offset) (* access simple var *)
else
with arg1↑.vari↑ do gtVarn := getVar(level,offset); (* access array var *)
end;
procedure getFrame (f: framep; r: nodep); forward;
procedure getVal (level, offset: byte);
var entry: enventryp; res: nodep;
begin
entry := getVar(level,offset); (* look up environment entry for variable *)
res := newNode;
res↑.ntype := leafnode;
res↑.ltype := entry↑.etype; (* copy datatype of result *)
if entry↑.etype = svaltype then res↑.s := entry↑.s (* it's a scalar *)
else if entry↑.etype <> frametype then (* it's a vector, trans or string *)
with res↑ do
begin
v := entry↑.v; (* copy pointer *)
str := entry↑.str;
if v = nil then
if ltype = vectype then v := nilvect
else if ltype = transtype then t := niltrans
else length := 0;
(* complain??? *)
end
else
begin
res↑.ltype := transtype;
getFrame(entry↑.f,res);
end;
push(res); (* store the value on the stack *)
end;
procedure change (f: framep; res: nodep); forward;
procedure setVal (level, offset: byte);
var entry: enventryp; res: nodep;
begin
entry := getVar(level,offset); (* look up environment entry for variable *)
res := pop; (* pop value off of stack *)
with entry↑ do
if etype = svaltype then s := res↑.s (* it's a scalar *)
else if etype = vectype then
begin
with res↑.v↑ do refcnt := refcnt + 1; (* indicate new vector is in use *)
if v <> nil then
begin
v↑.refcnt := v↑.refcnt - 1; (* we're done with vector now *)
if v↑.refcnt <= 0 then relVector(v); (* release it if no one wants it *)
end;
v := res↑.v; (* copy new vector pointer *)
end
else if etype = transtype then upTrans(t,res↑.t) (* update trans with new value *)
else if etype = strngtype then
begin
length := res↑.length;
str := res↑.str; (* copy new string pointer *)
end
else change(f,res); (* change frame's value, updating affixed frames *)
relNode(res); (* free node up *)
end;
function getNval(n: nodep; var b: boolean): nodep;
begin
b := false;
with n↑ do
if (ntype <> leafnode) or (ltype = varitype) then
begin n := pop; b := true end;
if n <> nil then
if n↑.ltype = pconstype then
begin n := n↑.pcval; b := false end;
getNval := n;
end;
(* aux routines: getPdb, freePdb, getEvent, freeEvent *)
function getPdb: pdbp;
var p: pdbp;
begin
p := newPdb;
with p↑ do
begin (* initialize it somewhat *)
nextPdb := allPdbs;
allPdbs := p; (* add us to list of all processes *)
next := nil;
if curInt <> nil then
begin
env := curInt↑.env;
level := getELev(env) + 1;
priority := curInt↑.priority;
cm := curInt↑.cm;
end
else
begin
env := sysEnv;
level := 1;
priority := 0;
cm := nil;
end;
status := nullqueue;
mode := 0;
spc := nil;
epc := nil;
sp := nil;
mech := nil;
procp := false;
evt := nil;
end;
getPdb := p;
end;
procedure freePdb(p: pdbp);
var po: pdbp; b: boolean;
begin (* remove pdb from list *)
if allPdbs = p then allPdbs := p↑.nextPdb
else
begin
po := allPdbs;
b := false;
repeat (* find pdb in list *)
if po↑.nextPdb = p then b := true else po := po↑.nextPdb
until b or (po = nil);
if b then po↑.nextPdb := p↑.nextPdb; (* splice us out of list *)
(* *** else complain??? *** *)
end;
relPdb(p);
end;
function getEvent: eventp;
var e: eventp;
begin
e := newEvent;
e↑.next := allEvents; (* add to list of all events *)
allEvents := e;
e↑.count := 0;
e↑.waitlist := nil;
getEvent := e;
end;
procedure freeEvent(e: eventp);
var eo: eventp; b: boolean;
begin (* remove event from list *)
if allEvents = e then begin allEvents := e↑.next; b := true end
else
begin
eo := allEvents;
b := false;
repeat (* find event in list *)
if eo↑.next = e then b := true else eo := eo↑.next
until b or (eo = nil);
if b then eo↑.next := e↑.next; (* splice us out of list *)
end;
if b then relEvent(e); (* if not in list already released *)
end;
(* graph structure routines: nextTime, getFrame, getDevice, feval, eval, change, invalidate, stvals ... *)
procedure nextTime;
begin
if etime = Maxint then etime := 1 (* should reset all invalid frames, but ... *)
else etime := etime + 1;
end;
procedure eval (f: framep);
var calc: nodep; b: boolean; f2, tr: transp;
begin
if f↑.valid <> etime then (* Haven't looked at it yet *)
begin
f↑.valid := etime; (* Mark it *)
calc := f↑.calcs; (* Get list of calculators *)
b := true;
while (calc <> nil) and b do (* See if someone it's affixed to is now valid *)
if (calc↑.ntype = calcnode) and (calc↑.rigid or calc↑.frame1) then
with calc↑.other↑ do (* A possibility, look at other frame *)
begin
if not ftype then (* See if it's a device or frame *)
begin (* It's a device - use it to compute current value *)
f2 := whereArm(mech); (* Get current device pos *)
b := false; (* No need to look further *)
end
else if (dcntr=0) and (valid=0) then (* not dynamic & valid frame *)
begin f2 := val; b := false end
else calc := calc↑.next (* dynamic or not valid - try next *)
end
else calc := calc↑.next; (* not a calc, or nonrigid and frame2 - try next *)
if calc = nil then
begin (* Check calcs again - this time trying to evaluate other frame *)
calc := f↑.calcs;
b := true;
while (calc <> nil) and b do
if (calc↑.ntype = calcnode) and (calc↑.rigid or calc↑.frame1) then
begin
eval(calc↑.other); (* Try to get a value for it *)
if calc↑.other↑.valid=0 then (* Is it now valid? *)
begin f2 := calc↑.other↑.val; b := false end (* Yes - all done *)
else calc := calc↑.next (* still not valid - try next *)
end
else calc := calc↑.next; (* not a calc, or nonrigid and frame2 - try next *)
end;
if calc <> nil then
with calc↑ do
begin (* use other frame to evaluate desired one & return success *)
if tvarp then tr := tvar↑.t else tr := tval; (* explicitly named trans? *)
if not frame1 then tr := tinvrt(tr); (* second := inv(trans) * first *)
upTrans(f↑.val,ttmul(tr,f2)); (* first := trans * second *)
f↑.valid := 0; (* Mark it as now valid *)
end;
end;
end;
function feval (f: framep): transp;
begin
if not f↑.ftype then
begin (* If device use its current value *)
feval := whereArm(f↑.mech); (* Get current device pos *)
end
else (* frame *)
begin
if (f↑.dcntr<>0) or (f↑.valid<>0) then (* dynamic frame or not valid? *)
begin (* Need to calculate current value *)
nextTime; (* update eval time *)
eval(f); (* try to evaluate the variable *)
end;
if f↑.valid = 0 then feval := f↑.val (* copy trans pointer *)
else feval := niltrans; (* but always return something *)
end;
end;
function invalidate (f: framep): boolean;
var calc: nodep; b: boolean;
begin
(* invalidate frame & all other frames affixed either rigidly or
non-rigidly with this being frame2,
else indicate we need to modify non-rigid trans. *)
b := false; (* assume no updating of non-rigid relationships *)
if etime <> f↑.valid then (* haven't marked this one yet *)
with f↑ do
begin
if valid = 0 then upTrans(val,nil); (* flush old value *)
valid := etime; (* mark us as having an invalid value *)
calc := calcs;
while calc <> nil do (* invalidate everyone we're affixed to *)
begin (* rigidly or if we're frame 2 *)
if (calc↑.ntype = calcnode) and (calc↑.rigid or (not calc↑.frame1))
then b := b or invalidate(calc↑.other) (* go invalidate frame *)
else b := true; (* found a non-rigid affixment to update *)
calc := calc↑.next; (* now repeat with next calc *)
end;
end;
invalidate := b;
end;
procedure stvals (f: framep);
var calc,c2: nodep; t,val: transp; f2: framep;
begin
calc := f↑.calcs;
val := f↑.val; (* frames current value *)
while calc <> nil do (* update everyone we're affixed to *)
with calc↑ do
begin
f2 := other;
if (ntype = calcnode) and (rigid or (not frame1)) then
begin (* see if we need to update this frame *)
if f2↑.valid <> 0 then (* haven't updated it yet *)
begin
if tvarp then t := tvar↑.t else t := tval; (* explicitly named trans? *)
if frame1 then t := tinvrt(t); (* second := inv(trans) * first *)
upTrans(f2↑.val,ttmul(t,val)); (* first := trans * second *)
f2↑.valid := 0; (* Mark it as now valid *)
stvals(f2); (* and go update its affixments *)
end
end
else
begin (* need to update relation trans *)
t := feval(f2); (* get a value for f2 *)
t := ttmul(val,tinvrt(t)); (* compute new relation trans *)
if tvarp then upTrans(tvar↑.t,t)
else
begin
upTrans(tval,t); (* store it *)
c2 := f2↑.calcs; (* now go fix trans up in f2's calc list *)
while c2↑.other <> f do c2 := c2↑.next; (* find other calc of pair *)
upTrans(c2↑.tval,t); (* copy trans to it too *)
end;
end;
calc := calc↑.next; (* move on to next one *)
end;
end;
procedure change (* f: framep; res: nodep *);
var calc: nodep; b: boolean;
begin
if f↑.dcntr=0 then (* if not dynamic *)
begin
nextTime;
b := invalidate(f); (* b = true if any non-rigid affixments need updating *)
f↑.val := res↑.t; (* copy trans pointer *)
f↑.val↑.refcnt:=f↑.val↑.refcnt + 1; (* mark trans in use *)
f↑.valid := 0; (* mark us as having a valid value *)
if b then stvals(f); (* go fix up the non-rigid relationships *)
end
else begin
pp20L('Can''t assign to dyna',20); pp10('mic frames',10); ppLine;
(* maybe also give name of frame?? *)
end;
end;
procedure getDevice (f: framep; r: nodep);
var i: integer; ev: enventryp; (* for sim ver *)
begin
if f↑.sdev then
with msg↑ do
begin
cmd := wherecmd;
dev := f↑.mech;
getReply; (* have ARM servo read in the hand/device value *)
r↑.s := val;
r↑.ltype := svaltype;
(* for simulation version *)
case dev of (* get device offset *)
YHANDDEV: i := 6;
BHANDDEV: i := 2;
GHANDDEV: i := 10;
RHANDDEV: i := 14;
DRIVERDEV: i := 16;
VISEDEV: i := 20;
end;
ev := getEntry(0,i);
r↑.s := ev↑.f↑.sdest; (* use where ever last move was to *)
end
else
r↑.t := whereArm(f↑.mech); (* go read in the arm's position *)
end;
procedure getFrame (* f: framep; r: nodep *);
begin
if not f↑.ftype then getDevice(f,r) (* If device get its current value *)
else (* frame *)
begin
if (f↑.dcntr<>0) or (f↑.valid<>0) then (* dynamic frame or not valid? *)
begin (* Need to calculate current value *)
nextTime; (* update eval time *)
eval(f); (* try to evaluate the variable *)
end;
r↑.t := f↑.val; (* copy trans pointer *)
if r↑.t = nil then r↑.t := niltrans; (* always return something *)
(* complain though??? *)
end;
end;
(* aux routines to create & destroy variables: enterEntry,makeCmon,makeVar,killVar,killEnv,killNode,killStack *)
function enterEntry (var i,j: integer; var env: environp;
envhdr: envheaderp; v: varidefp): enventryp;
var e: enventryp; k: integer;
begin
if j = 9 then (* need to allocate new environment record *)
begin
env↑.next := newEnvironment;
env := env↑.next;
env↑.next := nil;
for k := 0 to 9 do env↑.vals[k] := nil;
j := 0;
i := i + 1;
if i < 5 then envhdr↑.env[i] := env;
end
else j := j + 1;
k := 10 * i + j;
if k > envhdr↑.varcnt then envhdr↑.varcnt := k;
e := newEentry; (* get an environment entry for the variable *)
env↑.vals[j] := e;
e↑.etype := v↑.vtype; (* copy datatype of variable *)
if e↑.etype = rottype then e↑.etype := transtype; (* rots are transes internally *)
enterEntry := e;
end;
procedure makeCmon(e: enventryp; vari: varidefp);
var c: cmoncbp;
begin
c := newCmoncb;
with c↑ do
begin
cmon := vari↑.s; (* point to cmon definition *)
enabled := false;
running := false;
pdb := getPdb; (* get us a pdb for later *)
oldcmon := e↑.c; (* remember if we're pushing anyone *)
if c↑.cmon↑.oncond↑.ntype = forcenode then
evt := getEvent (* we'll need an event later *)
else evt := nil;
end;
with c↑.pdb↑ do
begin (* set up pdb *)
priority := (priority mod 10) + 1; (* base level priority *)
spc := c↑.cmon;
sdef := spc;
cm := c; (* point to cmon def *)
opdb := curInt; (* pointer to parent pdb so we can get mech bits *)
end;
e↑.c := c;
end;
procedure makeVar(e: enventryp; vari: varidefp; tbits: integer);
var i,j,k,size: integer; envhdr: envheaderp; env: environp; ep: enventryp;
b,bo,bd: nodep;
function getBound (n: nodep): integer;
var e: enventryp;
begin
if n↑.ntype = exprnode then (* value on stack *)
begin n := pop; getBound := round(n↑.s) end
else if n↑.ltype = svaltype then getBound := round(n↑.s) (* constant val *)
else if n↑.ltype = pconstype then
getBound := round(n↑.pcval↑.s) (* predeclared constant *)
else
begin (* variable value *)
with n↑.vari↑ do e := getVar(level,offset);
getBound := round(e↑.s);
end;
end;
function getSize (b: nodep): integer;
begin
if b↑.next = nil then b↑.mult := 1
else b↑.mult := getSize(b↑.next);
getSize := b↑.mult * (b↑.ub - b↑.lb + 1);
end;
begin
with e↑ do
begin
if tbits = 1 then etype := arraytype
else if tbits = 2 then etype := proctype
else if tbits >= 4 then etype := reftype;
case etype of
svaltype: s := 0.0;
vectype,
transtype: v := nil;
frametype: begin
f := newFrame;
f↑.vari := vari;
f↑.calcs := nil;
f↑.ftype := true;
f↑.valid := -1;
f↑.val := nil;
f↑.fdepr := nil;
f↑.dcntr := 0;
f↑.dev := nil;
end;
eventtype: evt := getEvent;
strngtype: begin length := 0; str := nil end;
cmontype: begin
c := nil;
makeCmon(e,vari);
end;
proctype: begin
etype := proctype; (* fix up type field *)
p := vari↑.p;
penv := curInt↑.env;
end;
arraytype: begin
bd := vari↑.a↑.bounds;
bo := nil;
while bd <> nil do (* bind the array bounds *)
begin
b := newNode;
if bo = nil then e↑.bnds := b else bo↑.next := b;
bo := b;
b↑.ntype := bndvalnode;
b↑.lb := getBound(bd↑.lower);
b↑.ub := getBound(bd↑.upper);
bd := bd↑.next
end;
size := getSize(e↑.bnds);
envhdr := newEheader;
envhdr↑.varcnt := 0;
e↑.a := envhdr;
env := newEnvironment;
env↑.next := nil;
envhdr↑.env[0] := env;
for j := 1 to 4 do envhdr↑.env[j] := nil;
for j := 0 to 9 do env↑.vals[j] := nil;
i := 0;
j := -1;
for k := 1 to size do
begin
ep := enterEntry(i,j,env,envhdr,vari);
makeVar(ep,vari,0); (* make variable environment entry *)
end;
for i := j+1 to 9 do env↑.vals[i] := nil;
end;
end;
end;
end;
procedure unfix(f1,f2: framep); forward;
procedure killVar(e: enventryp);
var j,k,size: integer; envhdr: envheaderp; env,eo: environp; ep: enventryp;
b,bo: nodep; pp: pdbp; cp: cmoncbp;
begin
with e↑ do
case etype of
(* don't need to do anything for scalars & strings *)
vectype: if v <> nil then (* check for old value *)
begin
v↑.refcnt := v↑.refcnt - 1; (* we're done with vector now *)
if v↑.refcnt <= 0 then relVector(v); (* release it if no one else wants it *)
end;
transtype: upTrans(t,nil);
frametype: begin
while f↑.calcs <> nil do
unfix(f,f↑.calcs↑.other); (* unfix us from everyone *)
upTrans(f↑.val,nil); (* flush our current value *)
relFrame(f); (* flush frame *)
end;
eventtype: begin
(* *** what to do with those processes waiting on this event? *** *)
pp := evt↑.waitlist;
while pp <> nil do
begin pp↑.status := nullqueue; pp := pp↑.next end;
freeEvent(evt);
end;
cmontype: repeat
if c↑.cmon↑.oncond↑.ntype = forcenode then freeEvent(c↑.evt);
freePdb(c↑.pdb); (* now it's ok to flush its pdb *)
cp := c↑.oldcmon; (* did we have several copies active? *)
relCmoncb(c); (* and also free up its cmoncb *)
c := cp;
until cp = nil;
arraytype: begin
b := e↑.bnds;
size := b↑.mult * (b↑.ub - b↑.lb + 1); (* get array size *)
while b <> nil do begin bo := b; b := b↑.next; relNode(bo) end;
envhdr := e↑.a;
env := envhdr↑.env[0];
relEheader(envhdr);
j := -1;
for k := 1 to size do
begin
if j = 9 then
begin eo := env; env := env↑.next; relEnvironment(eo); j := 0 end
else j := j + 1;
ep := env↑.vals[j];
killVar(ep); (* kill variable environment entry *)
end;
relEnvironment(env);
end;
(* nothing to do for procedures or indirect references *)
end;
relEentry(e);
e := nil;
end;
procedure killEnv;
var envhdr: envheaderp; envir,eo: environp; e: enventryp; j: integer;
begin
if (curInt↑.env <> sysEnv) and (curInt↑.env↑.varcnt < 255) then
begin (* varcnt check is so flushall doesn't have us kill it twice *)
with curInt↑ do
begin
envhdr := env;
env := envhdr↑.parent;
end;
envhdr↑.varcnt := 255;
envir := envhdr↑.env[0];
relEheader(envhdr);
j := 0;
while envir <> nil do (* deallocate variables *)
begin
e := envir↑.vals[j];
if e <> nil then killVar(e); (* kill var's environment entry *)
if j = 9 then
begin
eo := envir;
envir := envir↑.next;
relEnvironment(eo);
j := 0
end
else j := j + 1;
end;
end
else curInt↑.env := sysEnv;
end;
procedure killNode(n: nodep);
begin
with n↑ do
if ntype = leafnode then
case ltype of
vectype: if v↑.refcnt <= 0 then relVector(v);
transtype: if t↑.refcnt <= 0 then relTrans(t);
end;
relNode(n);
end;
procedure killStack;
var n,np: nodep;
begin
n := curInt↑.sp; (* top of stack *)
while n <> nil do
begin
np := n↑.next;
killNode(n);
n := np;
end;
end;
(* aux io routines: prntSval, prntVec, prntTrans, prntStrng, prntPlist, onum, prntVar, badjoints *)
procedure prntSval(s: real);
var si: real;
begin
if s < 32767 then (* max 16 bit integer *)
begin
si := trunc(s);
s := si + round(1000*(s-si))/1000;
end;
ppReal(s);
end;
procedure prntVec(v: vectorp);
var i: integer;
begin
pp10('vector( ',7);
with v↑ do
for i := 1 to 3 do
begin
prntSval(val[i]);
if i = 3 then ppChar(')') else ppChar(',');
end;
ppOutNow;
end;
procedure prntTrans(t: transp);
var i: integer; v: vectorp;
begin
with t↑ do
begin
refcnt := refcnt + 1;
pp10('trans(rot(',10);
v := taxis(t); prntVec(v); relVector(v);
ppChar(',');
prntSval(tmagn(t));
pp10('),vector( ',9);
for i := 1 to 3 do
begin prntSval(val[i,4]); if i = 3 then ppChar(')') else ppChar(',') end;
ppChar(')');
refcnt := refcnt - 1;
end;
ppLine;
end;
procedure prntStrng(length: integer; s: strngp);
begin
ppStrng(length,s);
ppOutNow;
end;
procedure prntPlist(n: nodep);
var np: nodep; b: boolean;
begin
while n <> nil do (* print out the list *)
begin
np := getNval(n↑.lval,b);
if np <> nil then
begin
with np↑ do
case ltype of
svaltype: begin prntSval(s); ppOutNow end;
vectype: prntVec(v);
transtype: prntTrans(t);
strngtype: prntStrng(length,str);
end;
if b then killNode(np); (* flush used stack entry *)
end;
n := n↑.next;
end;
end;
procedure onum(s: integer);
procedure onum1(s: integer);
var i,j: integer;
begin
i := s div 8;
j := s mod 8;
if i > 0 then onum(i);
ppInt(j);
end;
begin
if s < 0 then begin ppChar('-'); s := -s end;
onum1(s);
ppOutNow;
end;
procedure prntVar(v: nodep);
var i: integer; n,p: nodep;
begin
if v = nil then pp10('Noname ',6)
else if v↑.ntype = leafnode then
with v↑.vid↑ do ppStrng(length,name) (* print variable name *)
else
begin (* array ref *)
with v↑.arg1↑.vid↑ do ppStrng(length,name); (* print variable name *)
n := v↑.arg2;
ppChar('[');
while n <> nil do
begin
p := pop; (* get this subscript's value *)
i := round(p↑.s);
ppInt(i);
relNode(p);
n := n↑.next;
if n = nil then ppChar(']') else ppChar(',');
end;
end;
ppLine;
end;
procedure badJoints(angle: integer);
var i: integer;
begin
if angle <> 0 then
begin (* tell associated joint numbers *)
pp20(' joint(s) = ',14);
i := 1;
while angle <> 0 do (* decode them *)
begin
if odd(angle) then
begin
ppInt(i);
if angle > 1 then ppChar(',');
end;
angle := angle div 2;
i := i + 1;
end;
ppLine;
end;
end;
(* aux routines: addPdb, sleep, deClkQueue, msgDispatch, swap *)
procedure addPdb(var plist: pdbp; pn: pdbp);
var p,pp: pdbp; b: boolean;
begin
if plist = nil then
begin (* empty queue - we're it *)
plist := pn;
pn↑.next := nil;
end
else if plist↑.priority < pn↑.priority then
begin (* add us to start of queue *)
pn↑.next := plist;
plist := pn;
end
else
begin (* merge us into the queue *)
p := plist;
b := true;
while (p↑.next <> nil) and b do
if p↑.next↑.priority >= pn↑.priority then p := p↑.next else b := false;
pn↑.next := p↑.next;
p↑.next := pn;
end;
end;
procedure sleep(whenV: integer);
var w,n,np: nodep; p,pp: pdbp; b: boolean; ti: integer;
begin
curInt↑.next := nil;
np := clkQueue;
n := nil;
b := true;
ti := stime;
while np <> nil do
if ti = whenV then (* add us to this wait node *)
begin
addPdb(np↑.who,curInt);
np := nil;
b := false;
end
else if ti < whenV then
begin (* move down list *)
whenV := whenV - ti; (* update relative wait time *)
n := np;
np := np↑.next;
if np <> nil then ti := np↑.when;
end
else np := nil;
if b then (* need to make a new entry *)
begin
w := newNode;
with w↑ do
begin
ntype := waitlistnode;
who := curInt;
when := whenV;
next := nil;
end;
(* request a Marktime ast to have us made active *)
if n = nil then
begin
w↑.next := clkQueue;
clkQueue := w; (* first entry in queue *)
stime := whenv; (* hack for 10 *)
end
else
begin (* add us to the queue *)
w↑.next := n↑.next;
n↑.next := w;
end;
if w↑.next <> nil then w↑.next↑.when := w↑.next↑.when - whenV;
end;
curInt↑.status := sleepqueue;
curInt := nil; (* swap in someone else *)
resched := true;
end;
procedure deClkQueue(po: pdbp);
var n,np: nodep; p,pp: pdbp; b: boolean;
begin (* remove pdb from clock queue *)
n := clkQueue;
np := nil;
b := true;
while (n <> nil) and b do
begin
p := n↑.who;
pp := nil;
while (p <> nil) and (p <> po) do begin pp := p; p := p↑.next end;
if p <> nil then (* found us, now splice us out of the list *)
begin
b := false;
if pp = nil then
begin (* we were first entry in list *)
n↑.who := p↑.next;
if n↑.who = nil then (* check if we were only entry *)
begin (* yup - remove this wait list node *)
if np <> nil then np↑.next := n↑.next (* splice out node *)
else
begin (* we were first node *)
clkQueue := n↑.next;
if n↑.next = nil then stime := 0 (* clock queue empty now *)
else stime := stime + n↑.next↑.when; (* reset new wait time *)
end;
if n↑.next <> nil then n↑.next↑.when := n↑.when + n↑.next↑.when;
relNode(n); (* done with waitlist node now *)
end
end
else pp↑.next := p↑.next; (* splice us out of list *)
end
else begin np := n; n := n↑.next end; (* try next node *)
end;
end;
procedure msgDispatch; (* handles signals & movedone from ARM *)
var p: pdbp; nd: nodep;
begin
with msg↑ do
if cmd = errorcmd then
begin
if ok then pp20L('Fatal error: ',13)
else pp10L('Warning: ',9);
case dev of (* tell which device *)
yarmdev: pp10('yarm - ',7);
yhanddev: pp10('yhand - ',8);
barmdev: pp10('barm - ',7);
bhanddev: pp10('bhand - ',8);
visedev: pp10('vise - ',7);
driverdev: pp10('driver - ',9);
garmdev: pp10('garm - ',7);
ghanddev: pp10('ghand - ',8);
rarmdev: pp10('rarm - ',7);
rhanddev: pp10('rhand - ',8);
end;
case error of
noarmsol: begin pp20('No arm solution foun',20); pp20('d, will use approxim',20);
pp20('ate solution. ',13) end;
timerr: begin pp20('Specified motion tim',20); pp20('e is too short, will',20);
pp20(' try to use it anywa',20); pp5('y. ',2) end;
durerr: begin pp20('Motion overly constr',20); pp20('ained, will ignore g',20);
pp20('lobal time constrain',20); pp5('t. ',2) end;
toolong: begin pp20('Segment longer than ',20); pp20('32.6 seconds, will u',20);
pp20('se 32.6 seconds for ',20); pp10('length. ',7) end;
featna: begin pp20('Feature not availabl',20); pp10('e yet. ',6) end;
end;
ppLine;
badJoints(bits); (* tell which joint(s) were bad, if any *)
end
else
begin
evt↑.count := evt↑.count + 1;
p := evt↑.waitlist; (* get pdb of process to schedule (if any) *)
if p <> nil then
begin
evt↑.waitlist := p↑.next; (* remove node from waitlist *)
p↑.status := runqueue;
addPdb(activeInts,p); (* add it to active process list *)
if curInt = nil then resched := true
else
if p↑.priority > curInt↑.priority then
resched := true; (* swap it in and swap us out *)
if cmd = movedonecmd then
begin (* need to put error bits on stack *)
nd := newNode;
with nd↑ do
begin
ntype := leafnode;
ltype := svaltype;
if ok then s := 0 else s := 128 * ord(error) + bits;
(* *** s := bits; huh??? *** *)
next := p↑.sp; (* push it *)
p↑.sp := nd;
end;
freeEvent(evt); (* also need to reclaim event *)
end
else if cmd <> signalcmd then
begin pp20('Unknown message of t',20); pp5('ype: ',5);
ppInt(ord(cmd)); ppLine end;
end;
end;
end;
procedure swap(newp: pdbp);
var p,po: pdbp; b: boolean; e: eventp;
begin
if newp = nil then
begin (* swap in some active process *)
curInt := activeInts;
if activeInts <> nil then activeInts := activeInts↑.next;
end
else
begin
if newp↑.status = runqueue then
begin (* remove us from activeInts list *)
if activeInts = newp then activeInts := newp↑.next;
p := activeInts;
while p↑.next <> nil do
if p↑.next = newp then p↑.next := newp↑.next (* remove us *)
else p := p↑.next;
end
else if newp↑.status = sleepqueue then deClkQueue(newp)
else if newp↑.status = eventqueue then
begin (* run through all events & remove us from event queue *)
e := allEvents;
b := true;
while b and (e <> nil) do
with e↑ do
begin
if waitlist = newp then
begin waitlist := newp↑.next; b := false end
else
begin
p := waitlist;
while b and (p <> nil) do
if p↑.next = newp then
begin p↑.next := newp↑.next; b := false end
else p := p↑.next;
end;
if b then e := next else count := count + 1;
end;
end;
if (newp <> curInt) and (curInt <> nil) then
begin
curInt↑.status := runqueue;
addPdb(activeInts,curInt); (* swap current process out *)
end;
curInt := newp; (* make new guy active *)
newp↑.next := nil;
end;
if curInt <> nil then
begin curInt↑.status := nowrunning; curInt↑.next := nil end;
end;
(* aux routines: initArms,initWorld,consDef,passConstants,flushLevel,flushAll,unwind,flushPdb,flushKids *)
procedure initArms;
var b: boolean;
begin
(* initMsg(msg,msgp); (* connect to message buffer *)
new(msg); msg↑.ok := true; (* for simulation version *)
b := startArm; (* get ARM servo running *)
(* hand shaking code to start up ARM servo & do any needed calibrating *)
end;
procedure consDef;
begin
xhat := vmake(1,0,0); xhat↑.refcnt := 1000;
yhat := vmake(0,1,0); yhat↑.refcnt := 1000;
zhat := vmake(0,0,1); zhat↑.refcnt := 1000;
nilvect := vmake(0,0,0); nilvect↑.refcnt := 1000;
niltrans := tmake(vsaxwr(zhat,0.0),nilvect); niltrans↑.refcnt := 1000;
ypark := tmake(vsaxwr(yhat,180.0),vmake(43.5,2.325,6.86));
bpark := tmake(vsaxwr(yhat,180.0),vmake(43.53125,56.855,9.95875));
gpark := tmake(vsaxwr(zhat,180.0),vmake(83.2,46.13,67.7));
rpark := tmake(niltrans,vmake(84.8,12.87,67.7));
ypark↑.refcnt := 1000;
bpark↑.refcnt := 1000;
gpark↑.refcnt := 1000;
rpark↑.refcnt := 1000;
end;
procedure passConstants(var x,y,z,nv: vectorp; var b,yp,g,r,nt: transp);
begin
x := xhat; y := yhat; z := zhat; nv := nilvect;
b := bpark; yp := ypark; g := gpark; r := rpark; nt := niltrans;
end;
procedure initWorld;
var v: varidefp; e: enventryp; i,j: integer; envir: environp;
b: boolean;
begin
initArms; (* *** should this go here ??? *** *)
etime := 0;
curtime := 0;
activeInts := nil; (* zero the various queues *)
clkQueue := nil;
readQueue := nil;
allPdbs := nil;
curInt := nil;
allEvents := nil;
resched := false;
singleThreadMode := false;
sysEnv := newEheader; (* set up system variables *)
with sysEnv↑ do
begin
parent := nil;
block := nil;
procp := false;
envir := newEnvironment;
env[0] := envir;
for i := 1 to 4 do env[i] := nil;
end;
i := 0;
j := -1;
v := getsysVars; (* get list of predefined system variables *)
while v <> nil do
begin
(* need to handle devices specially - especially scalar devices *)
e := enterEntry(i,j,envir,sysEnv,v);
b := v↑.offset in [0,2,4,6,8,10,12,14,16,20];
(* offsets: arms: 0,4,8,12 hands: 2,6,10,14 driver/vise: 16,20 *)
if b then e↑.etype := frametype; (* so we get a frame for scalar devices *)
makeVar(e,v,v↑.tbits); (* make variable environment entry *)
if b then (* set up device values *)
with e↑.f↑ do
begin
ftype := false; (* it's a device *)
sdev := v↑.vtype = svaltype; (* indicate if scalar *)
if sdev then sdest := 0
else
begin
tdest := niltrans;
appr := nil;
depr := nil;
end;
case v↑.offset div 2 of (* set Mechanism bits *)
2: mech := YARMDEV; (* yarm *)
3: mech := YHANDDEV; (* yhand *)
0: mech := BARMDEV; (* barm *)
1: mech := BHANDDEV; (* bhand *)
10: mech := VISEDEV; (* vise *)
8: mech := DRIVERDEV; (* driver *)
4: mech := GARMDEV; (* garm *)
5: mech := GHANDDEV; (* ghand *)
6: mech := RARMDEV; (* rarm *)
7: mech := RHANDDEV; (* rhand *)
end;
end;
v := v↑.next
end;
for i := j+1 to 9 do envir↑.vals[i] := nil;
speedfactor := getEntry(0,24);
e := getEntry(0,0); (* offset for barm = 0 *)
barm := e↑.f; (* remember frame used for blue arm *)
curInt := getPdb;
escInit(escapeI); (* enable escape-I interrupts *)
end;
procedure flushLevel(dLev: integer); (* to clean up from debugger *)
var b: boolean; pri: integer; e: eventp; pp,po: pdbp; ee: enventryp;
begin
pri := dLev * 10;
if curInt <> nil then
if curInt↑.priority >= pri then curInt := nil;
b := true;
while b and (activeInts <> nil) do (* flush run queue *)
if activeInts↑.priority >= pri then activeInts := activeInts↑.next
else b := false;
b := true;
while b and (readQueue <> nil) do (* flush read queue *)
if readQueue↑.priority >= pri then readQueue := readQueue↑.next
else b := false;
e := allEvents;
while e <> nil do
with e↑ do
begin
b := true;
while b and (waitlist <> nil) do (* clean up event's waitlist *)
if waitlist↑.priority >= pri then
begin
waitlist := waitlist↑.next;
count := count + 1;
end
else b := false;
e := next;
end;
po := curInt;
pp := allPdbs;
while pp <> nil do
begin
curInt := pp;
pp := pp↑.nextPdb;
with curInt↑ do
if priority >= pri then (* may need to flush this one *)
begin
killStack;
while level < getELev(env) do killEnv; (* flush envs process created *)
if status = sleepqueue then deClkQueue(curInt);
if cm <> nil then
with cm↑ do
if oldcmon <> nil then
begin
with cmon↑.cdef↑ do ee := getVar(level,offset);
ee↑.c := oldcmon;
freePdb(pdb); (* done with this incarnation of cmon *)
if cmon↑.oncond↑.ntype = forcenode then freeEvent(evt);
relCmoncb(cm);
end
else
begin (* set us up for later *)
priority := (priority mod 10) + 1; (* base level priority again *)
spc := cm↑.cmon;
mode := 0;
status := nullqueue;
running := false;
enabled := false;
end
else
begin
if (not procp) and (evt <> nil) then freeEvent(evt);
freePdb(curInt);
end;
end;
end;
curInt := po;
end;
procedure flushAll(p: pdbp; dLev: integer); (* for use by EDIT *)
var b: boolean; i: integer; e: eventp; pp,po: pdbp;
begin
flushLevel(dLev);
if p <> nil then
begin (* flush process *)
po := curInt;
curInt := p;
with curInt↑ do
begin
killStack;
while level < getELev(env) do killEnv; (* flush envs process created *)
if status = sleepqueue then deClkQueue(curInt);
if cm = nil then relPdb(curInt);
end;
curInt := po;
end;
if dLev = 0 then
begin
etime := 0;
stime := 0;
curtime := 0;
curInt := nil;
activeInts := nil;
readQueue := nil;
resched := false;
(* *** would like to flush any leftover events, unless we saved outermost *** *)
(* *** environment - if we are then we can't.... *** *)
(* while allEvents <> nil do freeEvent(allEvents); (* flush any old events *)
e := allEvents; (* at least we can reset them though *)
while e <> nil do
with e↑ do
begin e↑.waitlist := nil; count := 0; e := next end;
curInt := getPdb;
speedfactor↑.s := 2.0; (* re-initialize speed_factor *)
singleThreadMode := false; (* reset no wait mode *)
(* ??? any other system defined variables need to be reset/reinitialized? ??? *)
end;
end;
procedure unwind(p: pdbp; eLev: integer); (* for use by EDIT *)
var po: pdbp;
begin
po := curInt;
curInt := p;
while eLev < getELev(curInt↑.env) do killEnv; (* unwind inner environments *)
curInt := po;
end;
procedure flushPdb(p: pdbp); (* for use by EDIT *)
var po: pdbp;
begin
if p↑.status = runqueue then
if activeInts = p then activeInts := p↑.next
else
begin
po := activeInts;
while (po↑.next <> nil) and (po↑.next <> p) do po := po↑.next;
if po <> nil then po↑.next := p↑.next;
end
else if p↑.status = inputqueue then
if readQueue = p then readQueue := p↑.next
else
begin
po := readQueue;
while (po↑.next <> nil) and (po↑.next <> p) do po := po↑.next;
if po <> nil then po↑.next := p↑.next;
end;
p↑.priority := 255; (* so we can free just this process using flushLevel *)
flushLevel(25);
end;
procedure flushKids(p: pdbp; zapit: boolean);
var pp: pdbp; b: boolean;
begin
if p↑.status = joinwait then
begin
b := false;
repeat
pp := allPdbs;
repeat (* find one of the threads *)
with pp↑ do
if (not procp) and (cm = nil) and (evt <> nil) then
if evt↑.waitlist = p then
begin flushKids(pp,true); pp := nil end; (* flush it *)
if pp <> nil then (* move on to next *)
begin pp := pp↑.nextPdb; b := pp = nil end;
until pp = nil;
until b; (* repeat til we find all of them *)
end
else if p↑.status = proccall then
begin
pp := allPdbs;
repeat
if pp↑.procp and (pp↑.opdb = p) then
begin flushKids(pp,true); pp := nil end (* flush it *)
else pp := pp↑.nextPdb;
until pp = nil;
end;
if zapit then flushPdb(p);
end;
(* aux routines: cmonEnable, cmonDisable, cmonCheck *)
procedure cmonEnable(e: enventryp);
var p: pdbp; b: boolean; pri: integer;
begin
with e↑.c↑ do
if (enabled or running) and ((pdb↑.priority mod 10) < debugLevel) then
makeCmon(e,cmon↑.cdef); (* push old & make another for this debug level *)
with e↑.c↑ do
if running then enabled := true (* if currently running, re-enable it *)
else if not enabled then (* is it currently enabled? *)
begin
enabled := true; (* now it is *)
pdb↑.status := runqueue;
pdb↑.priority := (pdb↑.priority mod 10) + (10 * debuglevel);
addPdb(activeInts,pdb); (* add cmon to list of active processes *)
if pdb↑.priority > curInt↑.priority then
resched := true; (* need to swap us out *)
end;
end;
procedure cmonDisable(c: cmoncbp);
var p,pp: pdbp; b: boolean; n,np: nodep;
begin
with c↑ do
begin
if enabled then (* is it currently enabled? *)
begin
enabled := false; (* disable it *)
if cmon↑.oncond↑.ntype = forcenode then
begin
with msg↑ do
begin
cmd := forceoffcmd;
bits := fbits;
evt := c↑.evt;
end;
sendCmd; (* tell force system to stop checking for this force condition *)
end;
if cmon↑.exprCm or (cmon↑.oncond↑.ntype = durnode) then deClkQueue(pdb)
else
begin (* remove pdb from event queue *)
p := evt↑.waitlist;
pp := nil;
while (p <> nil) and (p <> pdb) do begin pp := p; p := p↑.next end;
if p <> nil then (* found us, now splice us out of the list *)
if pp = nil then evt↑.waitlist := p↑.next else pp↑.next := p↑.next;
end;
pdb↑.next := nil;
end;
end;
end;
function cmonCheck: boolean;
var b: boolean; i: integer; env: environp; ev: enventryp;
begin (* make sure all cmon's in current environment have finished *)
b := true;
env := curInt↑.env↑.env[0]; (* point to first environment record *)
i := 0;
ev := env↑.vals[0];
while (ev <> nil) and b do
with ev↑ do
begin (* see if any cmons are running *)
if etype = cmontype then
begin (* found a cmon *)
if c↑.running then
b := c↑.pdb↑.priority >= curInt↑.priority (* is it running now? *)
else cmonDisable(c); (* if not disabled it *)
end;
i := i + 1;
if i <= 9 then ev := env↑.vals[i]
else
begin
i := 0;
env := env↑.next; (* use next env record *)
if env <> nil then ev := env↑.vals[0] else ev := nil;
end;
end;
cmonCheck := b; (* true if no cmons are now running *)
end;
(* expression evaluator: evalExp *)
procedure evalExp;
var res, n1, n2, n3: nodep; p: pdbp; i, j, tbits: integer; vfp: varidefp;
ep,epar: enventryp; envir: environp; envhdr: envheaderp; ch: ascii;
b, b1, b2, b3: boolean;
begin
with curInt↑.epc↑ do
begin
if ntype = leafnode then
if ltype = varitype then with vari↑ do getVal(level, offset)
else begin (* should only get here for constants, badops & subscripts *)
if ltype = pconstype then n1 := pcval else n1 := curInt↑.epc;
res:= newNode;
with res↑ do
begin
ntype := leafnode;
ltype := n1↑.ltype;
length := n1↑.length; (* this should work for all leaftypes *)
str := n1↑.str;
end;
push(res);
end
else if ntype = exprnode then
begin
n2 := nil; b2 := false;
n3 := nil; b3 := false;
if (op < ioop) or (op = adcop) or (op = dacop) then (* not a special op *)
begin (* pop appropriate number of args off of stack *)
n1 := getNval(arg1,b1); (* all ops have at least one arg *)
if arg2 <> nil then
begin
n2 := getNval(arg2,b2);
if arg3 <> nil then
begin
n3 := getNval(arg3,b3);
end;
end
end
else begin n1 := nil; b1 := false end;
if (op < specop) or (op = adcop) then (* make sure it's not a special op *)
begin
res := newNode;
res↑.ntype := leafnode;
if (op < vecop) or (ioop < op) then res↑.ltype := svaltype
else if op < transop then res↑.ltype := vectype
else res↑.ltype := transtype;
end;
case op of (* assumes correct args on stack *)
(* relations *)
sltop: if n1↑.s < n2↑.s then res↑.s := 1.0 else res↑.s := 0.0;
sleop: if n1↑.s <= n2↑.s then res↑.s := 1.0 else res↑.s := 0.0;
seqop: if n1↑.s = n2↑.s then res↑.s := 1.0 else res↑.s := 0.0;
sgeop: if n1↑.s >= n2↑.s then res↑.s := 1.0 else res↑.s := 0.0;
sgtop: if n1↑.s > n2↑.s then res↑.s := 1.0 else res↑.s := 0.0;
sneop: if n1↑.s <> n2↑.s then res↑.s := 1.0 else res↑.s := 0.0;
(* logical *)
notop: if n1↑.s = 0.0 then res↑.s := 1.0 else res↑.s := 0.0;
orop: if (n1↑.s <> 0) or (n2↑.s <> 0) then res↑.s := 1.0 else res↑.s := 0.0;
xorop: if (n1↑.s <> 0) <> (n2↑.s <> 0) then res↑.s := 1.0 else res↑.s := 0.0;
andop: if (n1↑.s <> 0) and (n2↑.s <> 0) then res↑.s := 1.0 else res↑.s := 0.0;
eqvop: if (n1↑.s <> 0) = (n2↑.s <> 0) then res↑.s := 1.0 else res↑.s := 0.0;
(* scalar ops *)
saddop: res↑.s := n1↑.s + n2↑.s;
ssubop: res↑.s := n1↑.s - n2↑.s;
smulop: res↑.s := n1↑.s * n2↑.s;
sdivop: res↑.s := n1↑.s / n2↑.s;
snegop: res↑.s := - n1↑.s;
sabsop: res↑.s := abs(n1↑.s);
sexpop: res↑.s := exp(n2↑.s * ln(n1↑.s));
maxop: if n1↑.s > n2↑.s then res↑.s := n1↑.s else res↑.s := n2↑.s;
minop: if n1↑.s < n2↑.s then res↑.s := n1↑.s else res↑.s := n2↑.s;
intop: res↑.s := round(n1↑.s);
idivop: res↑.s := round(n1↑.s) div round(n2↑.s);
modop: res↑.s := round(n1↑.s) mod round(n2↑.s);
(* functions *)
sqrtop: res↑.s := sqrt(n1↑.s);
logop: res↑.s := ln(n1↑.s);
expop: res↑.s := exp(n1↑.s);
timeop: res↑.s := curtime - n1↑.s; (* ** daytime? conversion to secs? ** *)
(* trig *)
sinop: res↑.s := sind(n1↑.s);
cosop: res↑.s := cosd(n1↑.s);
tanop: res↑.s := tand(n1↑.s);
asinop: res↑.s := asin(n1↑.s);
acosop: res↑.s := acos(n1↑.s);
atan2op: res↑.s := atan2(n1↑.s,n2↑.s);
(* vector ops *)
vdotop: res↑.s := vdot(n1↑.v,n2↑.v);
vmagnop: res↑.s := vmagn(n1↑.v);
unitvop: res↑.v := unitv(n1↑.v);
vaddop: res↑.v := vadd(n1↑.v,n2↑.v);
vsubop: res↑.v := vsub(n1↑.v,n2↑.v);
vnegop: res↑.v := svmul(-1.0,n1↑.v);
crossvop: res↑.v := vcross(n1↑.v,n2↑.v);
vmakeop: res↑.v := vmake(n1↑.s,n2↑.s,n3↑.s);
svmulop: res↑.v := svmul(n1↑.s,n2↑.v);
vsmulop: res↑.v := svmul(n2↑.s,n1↑.v);
vsdivop: res↑.v := vsdiv(n1↑.v,n2↑.s);
tvmulop: res↑.v := tvmul(n1↑.t,n2↑.v);
wrtop: res↑.v := tvmul(torient(n2↑.t),n1↑.v);
(* trans ops *)
tposop: res↑.v := tpos(n1↑.t);
taxisop: res↑.v := taxis(n1↑.t);
tmagnop: res↑.s := tmagn(n1↑.t);
fmakeop,
tmakeop: res↑.t := tmake(n1↑.t,n2↑.v);
torientop: res↑.t := torient(n1↑.t);
ttmulop: res↑.t := ttmul(n1↑.t,n2↑.t);
tvaddop: res↑.t := tvadd(n1↑.t,n2↑.v);
tvsubop: res↑.t := tvsub(n1↑.t,n2↑.v);
tinvrtop: res↑.t := tinvrt(n1↑.t);
vsaxwrop: res↑.t := vsaxwr(n1↑.v,n2↑.s);
constrop: res↑.t := construct(n1↑.v,n2↑.v,n3↑.v);
ftofop: res↑.t := ttmul(tinvrt(n1↑.t),n2↑.t);
vmkfrcop: res↑.t := vmkfrc(n1↑.v);
(* input ops *)
queryop: begin (* now print everything out *)
b := false;
if not inputReady then
if readQueue = nil then
begin (* first time through *)
prntplist(arg2);
b := true;
end
else if (readQueue↑.priority div 10) < (curInt↑.priority div 10) then
begin (* first time through *)
prntplist(arg2);
b := true;
end
else sleep(60) (* wait a sec for other input to finish *)
else
begin
inputReady := false;
ch := inputLine[1];
if ord(ch) > 140B then ch := chr(ord(ch)-40B); (* make upper case *)
if (ch = 'Y') or (ch = 'N') then
begin
if ch = 'Y' then res↑.s := 1.0 else res↑.s := 0.0;
push(res);
end
else b := true; (* ask again *)
end;
if b then
begin
relNode(res);
pp20L('Type Y or N: ',13);
ppOutNow;
curInt↑.next := readQueue;
readQueue := curInt; (* swap us out *)
curInt↑.status := inputqueue;
curInt := nil;
inputp := 0;
resched := true;
end
end;
inscalarop: begin
if not inputReady then
begin
if readQueue = nil then b := true
else b := (readQueue↑.priority div 10)<(curInt↑.priority div 10);
if b then
begin (* first time through *)
pp20L('Scalar please: ',15); ppOutNow;
curInt↑.next := readQueue;
readQueue := curInt; (* swap us out *)
curInt↑.status := inputqueue;
curInt := nil;
inputp := 0;
resched := true;
end
else sleep(60); (* wait a sec for other input to finish *)
relNode(res);
end
else
begin (* parse the number *)
inputReady := false;
b := true; (* assume plus *)
i := 1;
while (i <= inputp) and (inputLine[i] = ' ') do i := i + 1;
if inputLine[i] = '+' then i := i + 1
else if inputLine[i] = '-' then begin b := false; i := i + 1 end;
while (i <= inputp) and (inputLine[i] = ' ') do i := i + 1;
j := 0;
while (i <= inputp) and (* get integer part *)
('0' <= inputLine[i]) and (inputLine[i] <= '9') do
begin j := 10*j + ord(inputLine[i]) - ord('0'); i := i + 1 end;
res↑.s := j;
if inputLine[i] = '.' then
begin (* get fractional part *)
i := i + 1;
j := 10;
while (i <= inputp) and
('0' <= inputLine[i]) and (inputLine[i] <= '9') do
begin
res↑.s := res↑.s + (ord(inputLine[i]) - ord('0')) / j;
j := 10 * j;
i := i + 1;
end;
end;
if not b then res↑.s := - res↑.s;
push(res);
end;
end;
vmop: ;
adcop: with msg↑ do
begin
cmd := readadccmd;
n := round(n1↑.s); (* get channel # *)
if (n < 0) or (63 < n) then (* bad channel # *)
begin
pp20L('A/D channel out of r',20); pp20('ange - using chan 0 ',19);
ppLine;
n := 0;
end;
getReply; (* have ARM servo read it in *)
res↑.s := val; (* store result away *)
end;
dacop: with msg↑ do
begin
cmd := writedaccmd;
n := round(n1↑.s); (* get channel # *)
if (n < 1) or (4 < n) then (* bad channel # *)
begin
pp20L('D/A channel out of r',20); pp20('ange - using chan 1 ',19);
ppLine;
n := 1;
end;
val := n2↑.s; (* & magnitude *)
sendCmd; (* have ARM servo write it out *)
end;
(* special *)
arefop: with arg1↑.vari↑ do getVal(level,offset); (* should never get here *)
callop: begin
p := getPdb;
with p↑ do
begin
opdb := curInt;
procp := true;
status := nowrunning;
pdef := arg1↑.vari↑.p;
level := pdef↑.level;
spc := pdef↑.body; (* code to execute *)
end;
with arg1↑.vari↑ do
ep := getVar(level, offset); (* environment entry for procedure *)
envhdr := newEheader;
p↑.env := envhdr;
with envhdr↑ do
begin
parent := ep↑.penv; (* parent is env where proc defined *)
procp := true;
proc := ep↑.p;
varcnt := 0;
for j := 1 to 4 do env[j] := nil;
end;
vfp := ep↑.p↑.paramlist; (* formal parameters *)
n1 := arg2; (* actual parameters *)
envir := newEnvironment; (* always need at least one environment record *)
envir↑.next := nil;
envhdr↑.env[0] := envir;
for j := 0 to 9 do envir↑.vals[j] := nil;
i := 0;
j := -1;
while vfp <> nil do (* make parameter variables *)
begin
epar := enterEntry(i,j,envir,envhdr,vfp);
tbits := vfp↑.tbits;
if tbits = 4 then (* call by reference *)
with n1↑.lval↑ do
if ((ntype = exprnode) and (op <> arefop)) or (* expression *)
((ntype = leafnode) and (ltype <> varitype)) (* constant *)
then tbits := 0; (* change to call by value *)
makeVar(epar,vfp,tbits); (* make var's environment entry *)
with n1↑.lval↑ do (* now bind actual parameter value *)
if tbits = 5 then (* array passed by reference *)
with vari↑ do epar↑.r := getEntry(level,offset)
else if tbits = 4 then (* regular variable passed by reference *)
epar↑.r := gtVarn(n1↑.lval)
else (* need to copy value *)
begin
n2 := getNval(n1↑.lval,b);
with epar↑ do
case etype of
svaltype: s := n2↑.s;
vectype,
transtype: begin
v := n2↑.v;
v↑.refcnt := v↑.refcnt + 1;
end;
frametype: begin
f↑.val := n2↑.t;
f↑.valid := 0; (* mark us as valid *)
f↑.val↑.refcnt := f↑.val↑.refcnt + 1;
end;
strngtype: begin length := n2↑.length; str := n2↑.str end;
end;
if b then killNode(n2); (* done with stack entry *)
end;
n1 := n1↑.next;
vfp := vfp↑.next;
end;
for i := j+1 to 9 do envir↑.vals[i] := nil;
curInt↑.epc := curInt↑.epc↑.next; (* advance our epc now *)
curInt↑.status := proccall;
curInt := p; (* swap to procedure now *)
end;
badop: ;
end;
if op < ioop then push(res); (* save result on stack *)
if b1 then relNode(n1); (* release nodes when done with them *)
if b2 then relNode(n2);
if b3 then relNode(n3);
end
else if ntype <> listnode then
begin (* **** error - bad node **** *)
pp20L('Error in Eval - bad ',20); pp10('node type ',9); ppLine;
(* code to recover??? *)
end;
end;
if curInt <> nil then (* in case we're now waiting for input *)
with curInt↑ do (* advance pointer to next node to be evaluated *)
if epc <> nil then epc := epc↑.next;
end;
procedure doProg; (* ** ** *)
begin
(* *** stuff to reset affixments *** *)
speedfactor↑.s := 2.0; (* initialize speed_factor *)
barm↑.tdest := bpark; (* for 10 version *)
curInt↑.spc := curInt↑.spc↑.pcode;
curInt↑.mode := 0;
end;
procedure doBlock;
var i,j: integer; v: varidefp;
envhdr: envheaderp; e: enventryp; envir: environp;
begin
with curInt↑ do
begin
if spc↑.variables <> nil then
with spc↑ do
begin
envhdr := newEheader;
envhdr↑.parent := env;
env := envhdr;
envhdr↑.block := spc;
envhdr↑.varcnt := 0;
envhdr↑.procp := false;
envir := newEnvironment; (* always need at least one environment record *)
envir↑.next := nil;
envhdr↑.env[0] := envir;
for j := 1 to 4 do envhdr↑.env[j] := nil;
for j := 0 to 9 do envir↑.vals[j] := nil;
i := 0;
j := -1;
v := variables;
while v <> nil do
begin
if v↑.vtype < dimensiontype then
begin
e := enterEntry(i,j,envir,envhdr,v);
makeVar(e,v,v↑.tbits); (* make variable environment entry *)
end
else (* if v↑.vtype = freevartype then - need to do it for macros too *)
begin
relEentry(enterEntry(i,j,envir,envhdr,v)); (* space past env entry *)
envir↑.vals[j] := nil;
end;
v := v↑.next
end;
for i := j+1 to 9 do envir↑.vals[i] := nil;
end;
mode := 0;
spc := spc↑.bcode;
end;
end;
procedure doCoblock;
var e: eventp;
procedure sched(n: nodep);
var p: pdbp;
begin
if n↑.next <> nil then sched(n↑.next); (* maintain lexical order *)
if n↑.cstmnt↑.stype <> commenttype then
begin (* we don't want to schedule comments (yet) *)
p := getPdb; (* get a pdb for this thread *)
with p↑ do
begin
next := activeInts; (* add us to list of active interpreters *)
activeInts := p;
status := runqueue;
spc := n↑.cstmnt;
sdef := spc;
evt := e; (* event to signal when we go away *)
end;
end;
end;
begin
with curInt↑ do
case mode of
1: begin (* schedule the parallel threads for execution *)
mode := 2;
if spc↑.threads <> nil then
begin
e := getEvent; (* event to use for signalling when all threads are done *)
e↑.count := -spc↑.nthreads;
e↑.waitlist := curInt;
sched(spc↑.threads); (* schedule all the threads *)
curInt↑.status := joinwait;
curInt := nil;
resched := true; (* start up first of them *)
end;
end;
2: begin (* all threads are done - continue with main *)
mode := 0;
spc := spc↑.next;
end;
end;
end;
procedure doEnd;
var spcp: statementp; e: eventp; b: boolean;
begin
b := true;
with curInt↑ do
begin
spcp := spc↑.bparent;
case spcp↑.stype of
progtype: begin
running := false; (* all done running *)
mode := 0;
end;
blocktype: begin
if spcp↑.variables <> nil then (* any variables? *)
b := cmonCheck; (* any cmons now running? *)
if b then
begin (* no - we can clean things up *)
if spcp↑.variables <> nil then killEnv;
spcp := spcp↑.next;
mode := 0;
end
else sleep(30); (* give cmons time to finish *)
end;
coblocktype: begin
if evt = nil then
begin
running := false; (* break to debugger *)
(* *** if not singleThreadMode then complain??? *** *)
end
else
begin
b := false;
e := evt;
killStack; (* flush stack *)
freePdb(curInt);
if e↑.count = -1 then
begin (* this was last thread *)
curInt := e↑.waitlist; (* return to main *)
curInt↑.status := nowrunning;
freeEvent(e);
if activeInts <> nil then
if curInt↑.priority < activeInts↑.priority then
resched := true;
end
else
begin (* other threads still executing *)
e↑.count := e↑.count + 1;
curInt := nil; (* swap in someone else *)
resched := true;
end;
end;
end;
cmtype: begin (* terminate or resched this cmon *);
cm↑.running := false;
killStack;
b := false;
spc := spcp; (* set us up for next time *)
mode := 0;
if not cm↑.enabled then
begin (* we're done, swap us out *)
curInt↑.status := nullqueue;
curInt := nil; (* swap in someone else *)
resched := true;
end;
end;
fortype: begin
if sp↑.ntype <> forvalnode then (* gack! stack error *)
begin
pp20L('Can''t find FOR node ',20); pp20('- stack error!!! ',16);
ppLine;
(* could try to recover, but.... *)
end;
sp↑.fvar↑.s := sp↑.fvar↑.s + sp↑.fstep; (* next for value *)
mode := 2; (* do for check *)
end;
untiltype: mode := 2;
whiletype: mode := 0;
movetype, (* for error handler *)
iftype,
casetype: begin
spcp := spcp↑.next;
mode := 0;
end;
end;
if b then spc := spcp;
end;
end;
procedure doFor;
var ev: enventryp; fnode, res: nodep;
begin
with curInt↑ do
case mode of
1: begin (* stack contains: forvar subscripts, initial, step & final values *)
ev := gtVarn(spc↑.forvar); (* access variable *)
res := pop; (* get initial value *)
ev↑.s := res↑.s; (* store it away *)
relNode(res); (* release node *)
fnode := sp; (* get step value *)
fnode↑.ntype := forvalnode;
fnode↑.fstep := fnode↑.s; (* copy step value - note s & step fields may overlap *)
fnode↑.fvar := ev; (* copy environment entry *)
mode := 2;
end;
2: begin
fnode := sp;
if (fnode↑.fvar↑.s - fnode↑.next↑.s) * fnode↑.fstep <= 0.0 (* (cur-fin)*step *)
then spc:= spc↑.fbody (* go interpret for body *)
else begin
spc := spc↑.next; (* move on to next statement *);
res := fnode↑.next;
sp := res↑.next; (* pop for nodes off of stack *)
relNode(fnode); (* and release them *)
relNode(res);
end;
mode := 0;
end;
end;
end;
procedure doIf;
var res: nodep; s: statementp;
begin
with curInt↑ do
begin
res := pop; (* pop value off of stack *)
s := spc;
if res↑.s = 0.0 then spc := s↑.els else spc := s↑.thn;
if spc = nil then spc := s↑.next; (* if nil clause just go on to next stmnt *)
relNode(res);
mode := 0;
end;
end;
procedure doWhile;
var res: nodep;
begin
with curInt↑ do
begin
res := pop; (* pop value off of stack *)
if res↑.s = 0.0 then spc := spc↑.next (* if false move on to next stmnt *)
else if spc↑.body <> nil then spc := spc↑.body;
relNode(res);
mode := 0;
end;
end;
procedure doUntil;
var res: nodep;
begin
with curInt↑ do
case mode of
1: begin
if spc↑.body <> nil then begin spc := spc↑.body; mode := 0 end
else mode := 2;
end;
2: begin
epc := spc↑.exprs; (* need to evaluate until condition *)
mode := 3;
end;
3: begin
res := pop; (* pop value off of stack *)
if (res↑.s <> 0.0) then
begin
spc := spc↑.next; (* if true move on to next stmnt *)
mode := 0;
end
else mode := 1; (* if still false repeat body *)
relNode(res);
end;
end;
end;
procedure doCase;
var i: integer; p: nodep; spcp: statementp; b: boolean;
begin
with curInt↑ do
begin
p := pop; (* pop index value off of stack *)
i := round(p↑.s);
relNode(p);
spcp := nil;
p := spc↑.caselist;
if (i >= 0) and (i <= abs(spc↑.range)) then (* index within range *)
begin (* try to find proper case *)
b := true;
while (p <> nil) and b do
if (p↑.cval = i) then b := false else p := p↑.next;
if p <> nil then
begin spcp := p↑.stmnt; if spcp = nil then spcp := spc↑.next end
else if spc↑.range >= 0 then spcp := spc↑.next (* null statement *)
end;
if (spcp = nil) and (spc↑.range < 0) then
begin (* if none found and it's a labelled case statement check for else *)
p := spc↑.caselist;
b := true;
while (p <> nil) and b do (* search for else stmnt *)
if (p↑.cval = -1) then b := false else p := p↑.next;
if p <> nil then spcp := p↑.stmnt
end;
if spcp = nil then
begin
pp20L('Case index out of ra',20); pp5('nge: ',5); ppInt(i); ppLine;
spcp := spc↑.next;
end;
spc := spcp;
mode := 0;
end;
end;
procedure doCall;
var n: nodep;
begin
with curInt↑ do
begin
if spc↑.what↑.arg1↑.vari↑.vtype <> nulltype then (* flush unused result *)
n := pop;
mode := 0;
spc := spc↑.next; (* move on to next statement *);
end;
end;
procedure doReturn;
var p: pdbp; n: nodep; b,debRet: boolean;
begin
b := true;
with curInt↑ do
begin
if procp then debRet := false (* normal case *)
else if (priority > 9) and (nextpdb = nil) and (opdb <> nil) then
debRet := true (* immediately executed RETURN *)
else b := false; (* no good - nothing to return from *)
if debRet then p := opdb↑.opdb else p := opdb; (* pdb of caller *)
if b then
begin
while b and not env↑.procp do
begin (* make sure all cmon's in outer environments have finished *)
b := cmonCheck;
if b then killEnv; (* flush all environments out to parameters *)
end;
if b then (* no cmons now running *)
begin (* now we can clean things up & return from the procedure *)
if spc↑.retval <> nil then n := pop (* get return value *)
else n := nil;
if env↑.proc↑.ptype <> nulltype then
begin (* yes - put return value on caller's stack *)
if n <> nil then
if env↑.proc↑.ptype <> n↑.ltype then
begin
killNode(n);
n := nil;
end;
if n = nil then
begin
n := newNode;
with n↑ do (* use default value *)
begin
ntype := leafnode;
ltype := env↑.proc↑.ptype; (* copy datatype of result *)
if ltype = svaltype then s := 0.0 (* it's a scalar *)
else if ltype = vectype then v := nilvect
else if ltype = transtype then t := niltrans
else begin length := 0; str := nil end;
end;
end;
n↑.next := p↑.sp;
p↑.sp := n;
end;
killEnv; (* flush procedure's parameters too *)
killStack; (* flush stack *)
if debRet then
begin
opdb↑.opdb↑.status := runqueue;
addPdb(activeInts,opdb↑.opdb); (* re-activate caller *)
opdb↑.level := 255; (* so we don't re-release environments *)
flushKids(opdb,true); (* flush old procedure's pdb *)
spc := sdef↑.next; (* point to our abort *)
running := false; (* and return to debugger *)
end
else
begin
freePdb(curInt); (* flush procedure's pdb *)
curInt := p; (* all done - return *)
curInt↑.status := nowrunning;
end;
end
else sleep(30); (* give cmons time to finish *)
end
else
begin
pp20L('Ignoring return ',16); ppLine;
if spc↑.retval <> nil then n := pop; (* flush return value *)
spc := spc↑.next; (* just move on to next statement *)
mode := 0;
end;
end;
end;
procedure doPrint;
begin
with curInt↑ do
begin (* print everything out *)
prntplist(spc↑.plist);
mode := 0;
spc := spc↑.next;
end;
end;
procedure doPrompt;
var ch: ascii; b: boolean;
begin
with curInt↑ do
case mode of
1: begin
if readQueue = nil then b := true
else b := (readQueue↑.priority div 10) < (curInt↑.priority div 10);
if b then
begin (* first time through *)
prntplist(spc↑.plist);
mode := 2;
end
else sleep(60) (* wait a sec for other input to finish *)
end;
2: begin
pp20L('Type P to proceed: ',19);
ppOutNow;
mode := 3;
curInt↑.next := readQueue;
readQueue := curInt; (* swap us out *)
curInt↑.status := inputqueue;
curInt := nil;
inputp := 0;
resched := true;
end;
3: begin
inputReady := false;
if (inputLine[1] = chr(160B)) or (inputLine[1] = 'P') then
begin
mode := 0;
spc := spc↑.next;
end
else mode := 2; (* try again *)
end;
end;
end;
procedure doPause;
var i: integer; n: nodep;
begin
n := pop;
i := round(n↑.s * 60); (* get pause time (in 60Hz ticks) *)
relNode(n);
curInt↑.mode := 0; (* get ready for next statement *)
curInt↑.spc := curInt↑.spc↑.next;
sleep(i); (* put us to sleep for a while *)
end;
procedure doAbort;
begin
with curInt↑ do
begin (* print everything out *)
if spc↑.debugLev = 0 then
begin (* a real abort *)
(* tell arm servo to abort all motions in progress *)
(*
{$C .MCALL SETF$S
SETF$S #40. ;Signal Aborts by setting common event flag
}
*)
(* msg↑.cmd := abortcmd; *) (* latter we'll do it with messages *)
(* sendCmd; *)
prntplist(spc↑.plist);
spc := spc↑.next;
pp10L('Aborting ',8);
running := false; (* break to debugger *)
end
else if debugLevel = spc↑.debugLev then
running := false (* break if debugger process *)
else spc := spc↑.next; (* just ignore it *)
mode := 0;
end;
end;
procedure doAssign;
var ev: enventryp; res: nodep;
begin
with curInt↑.spc↑.what↑ do
begin
if ntype = leafnode then
with vari↑ do setVal(level,offset) (* store into simple variable *)
else
case op of (* see what type of store we're to do *)
arefop: with arg1↑.vari↑ do setVal(level,offset); (* store into array var *)
deproachop: begin (* any subscripts & deproach value on stack *)
ev := gtVarn(curInt↑.spc↑.what); (* access variable *)
res := pop; (* get deproach value *)
(* check we've really got a frame? *)
ev↑.f↑.fdepr := res↑.t; (* store it away *)
relNode(res);
end;
tposop,
torientop: begin
with arg1↑ do
if ntype = leafnode then
with vari↑ do setVal(level,offset) (* simple variable *)
else
with arg1↑.vari↑ do setVal(level,offset); (* array variable *)
end;
end;
curInt↑.mode := 0;
curInt↑.spc := curInt↑.spc↑.next; (* move on to next statement *);
end;
end;
procedure doSignal;
var ev: enventryp; p, pt: pdbp; st: statementp;
begin
with curInt↑ do
begin
st := spc;
spc := spc↑.next; (* advance our pc now before possibly swapping ourself out *)
mode := 0;
if singleThreadMode then
begin
pp20L('Would signal event: ',20); prntVar(st↑.event);
end
else if st↑.event <> nil then
begin
ev := gtVarn(st↑.event); (* access variable *)
ev↑.evt↑.count := ev↑.evt↑.count + 1;
p := ev↑.evt↑.waitlist; (* get pdb of process to schedule (if any) *)
if p <> nil then
begin
ev↑.evt↑.waitlist := p↑.next; (* remove node from waitlist *)
if p↑.priority > priority then
begin (* swap it in and swap us out *)
p↑.status := nowrunning;
pt := curInt;
curInt := p;
p := pt;
end;
p↑.status := runqueue;
addPdb(activeInts,p); (* add whoever to active process list *)
end;
end;
end;
end;
procedure doWait;
var ev: enventryp; p: pdbp; st: statementp; b: boolean;
begin
with curInt↑ do
if singleThreadMode then
if mode = 1 then
begin
if readQueue = nil then b := true
else b := (readQueue↑.priority div 10) < (curInt↑.priority div 10);
if b then
begin (* first time through *)
pp20L('Would wait for event',20); pp5(': ',2); prntVar(spc↑.event);
mode := 2;
doPrompt; (* now have user type a "P" to proceed *)
end
else sleep(60) (* wait a sec for other input to finish *)
end
else doPrompt
else
begin
st := spc;
spc := spc↑.next; (* advance our pc now before maybe swapping out *)
mode := 0;
if st↑.event <> nil then
begin
ev := gtVarn(st↑.event); (* access variable *)
ev↑.evt↑.count := ev↑.evt↑.count - 1;
if ev↑.evt↑.count < 0 then (* hasn't been signalled yet, need to wait *)
begin
curInt↑.status := eventqueue;
addPdb(ev↑.evt↑.waitlist,curInt); (* add us to wait list *)
curInt := nil; (* swap in someone else *)
resched := true;
end;
end;
end;
end;
procedure doEnable;
begin
with curInt↑ do
begin
if spc↑.cmonlab = nil then
if cm <> nil then cm↑.enabled := true (* re-enabling this cmon *)
else
begin
pp20L('No cmon to enable! ',18); ppLine;
end
else
begin
with spc↑.cmonlab↑.s↑.cdef↑ do
cmonEnable(getVar(level,offset)); (* enable cmon control block *)
end;
mode := 0;
spc := spc↑.next;
end;
end;
procedure doDisable;
var e: enventryp;
begin
with curInt↑ do
begin
if spc↑.cmonlab = nil then
if cm <> nil then cm↑.enabled := false (* disabling this cmon *)
else
begin
pp20L('No cmon to disable! ',19); ppLine;
end
else
begin
with spc↑.cmonlab↑.s↑.cdef↑ do
e := getVar(level,offset); (* get cmon control block *)
if e↑.c↑.running then sleep(30) (* if running wait for it to finish *)
else
begin
cmonDisable(e↑.c); (* disable it *)
mode := 0;
spc := spc↑.next;
end;
end;
end;
end;
(* affixment auxiliary routines: affixaux, unfixaux & unfix *)
procedure affixaux (f, d: framep; cnt: integer);
var c1,c2,ct: nodep;
begin
with f↑ do
if not (ftype and (dev <> nil)) then (* haven't marked it yet *)
begin
if not ftype then cnt := 1 (* it's a device *)
else begin dev := d; dcntr := cnt; cnt := cnt + 1; end; (* mark frame *)
c1 := calcs;
ct := nil;
while c1 <> nil do
begin (* mark everyone it's affixed to *)
if c1↑.rigid or not c1↑.frame1 then affixaux(c1↑.other,d,cnt)
else if c1↑.other↑.dev = nil then
begin (* need to break non-rigid affixment *)
(* first splice calcs out of affixment lists *)
if ct = nil then calcs := c1↑.next else ct↑.next := c1↑.next;
c2 := c1↑.other↑.calcs;
ct := nil;
while c2↑.other <> f do begin ct := c2; c2 := c2↑.next; end;
if ct = nil then c1↑.other↑.calcs := c2↑.next else ct↑.next := c2↑.next;
if not c1↑.tvarp then
begin (* release relation trans *)
upTrans(c1↑.tval,nil);
upTrans(c2↑.tval,nil);
end;
relNode(c1); (* finally release calc nodes *)
relNode(c2);
c1 := ct;
end;
ct := c1;
c1 := c1↑.next;
end;
end;
end;
function unfixaux (f: framep; cnt: integer): boolean;
var c: nodep; b: boolean; d: framep;
begin
b := false;
with f↑ do
if not ftype then affixaux(f,f,1) (* a device - remark everyone as dynamic *)
else if dev <> nil then (* check we're still marked as dynamic, else done *)
if cnt > dcntr then
begin
d := dev; dev := nil; (* so affixaux will mark us *)
affixaux(f,d,dcntr); (* need to remark everyone *)
end
else
begin (* unmark us *)
dev := nil;
dcntr := 0;
b := true;
c := calcs;
while (c <> nil) and b do
begin
b := unfixaux(c↑.other,cnt);
c := c↑.next
end
end;
unfixaux := b;
end;
procedure unfix (* f1,f2: framep *);
var t: transp; c1, c2: nodep; b: boolean; i: integer;
begin
if f1↑.ftype then t := feval(f1); (* try to get a value for both *)
if f2↑.ftype then t := feval(f2); (* if they're frames *)
c1 := f1↑.calcs; (* unfix f1 from f2 *)
c2 := nil;
b := true;
while (c1 <> nil) and b do
if c1↑.other = f2 then
begin (* found calc - splice it out of list *)
b := false;
if c2 = nil then f1↑.calcs := c1↑.next else c2↑.next := c1↑.next;
if not c1↑.tvarp then upTrans(c1↑.tval,nil); (* release old trans values *)
relNode(c1); (* done with calc node *)
end
else begin c2 := c1; c1 := c1↑.next end; (* try next *)
c1 := f2↑.calcs; (* now unfix f2 from f1 *)
c2 := nil;
b := true;
while (c1 <> nil) and b do
if c1↑.other = f1 then
begin (* found calc - splice it out of list *)
b := false;
if c2 = nil then f2↑.calcs := c1↑.next else c2↑.next := c1↑.next;
if not c1↑.tvarp then upTrans(c1↑.tval,nil); (* release old trans values *)
relNode(c1); (* done with calc node *)
end
else begin c2 := c1; c1 := c1↑.next end; (* try next *)
if not f1↑.ftype then b := unfixaux(f2,0) (* f2 no longer dynamic *)
else if not f2↑.ftype then b := unfixaux(f1,0) (* f1 no longer dynamic *)
else if f1↑.dev <> nil then (* both currently dynamic *)
if f1↑.dcntr < f2↑.dcntr then b := unfixaux(f2,f1↑.dcntr) (* unmark f2 *)
else b := unfixaux(f1,f2↑.dcntr); (* unmark f1 *)
end;
procedure doAffix;
var f1, f2: framep; ev: enventryp; c1, c2: nodep; t: transp; b: boolean;
begin
with curInt↑ do
begin (* stack has subscripts for frame1, frame2 & byvar & atexp value *)
ev := gtVarn(spc↑.frame1); (* access variable *)
f1 := ev↑.f;
ev := gtVarn(spc↑.frame2); (* access variable *)
f2 := ev↑.f;
if spc↑.byvar <> nil then
ev := gtVarn(spc↑.byvar) (* access variable *)
else ev := nil;
if spc↑.atexp <> nil then
begin
c1 := pop; (* get at expression value *)
t := c1↑.t; (* save it for later *)
relNode(c1); (* release node *)
end
else t := ttmul(feval(f1),tinvrt(feval(f2))); (* need to compute it *)
c1 := f1↑.calcs; (* see if frames are already affixed *)
b := true;
while b and (c1 <> nil) do
if c1↑.other = f2 then b := false else c1 := c1↑.next;
if c1 <> nil then (* currently affixed *)
begin
c2 := f2↑.calcs; (* find its mate *)
while c2↑.other <> f1 do c2 := c2↑.next;
if (not c1↑.tvarp) and (spc↑.byvar <> nil) then
begin (* if old affixment was direct and new one isn't *)
upTrans(c1↑.tval,nil); (* release old trans values *)
upTrans(c2↑.tval,nil);
end;
end
else
begin (* get a pair of calc nodes *)
c1 := newNode;
c2 := newNode;
c1↑.ntype := calcnode; (* indicate that we're a calc *)
c2↑.ntype := calcnode;
c1↑.other := f2; (* fill in other field *)
c2↑.other := f1;
c1↑.next := f1↑.calcs; (* link us to list of calcs *)
f1↑.calcs := c1;
c2↑.next := f2↑.calcs;
f2↑.calcs := c2;
c1↑.tval := nil; (* don't have a value yet *)
c2↑.tval := nil;
end;
c1↑.frame1 := true; (* say who's who *)
c2↑.frame1 := false;
c1↑.rigid := spc↑.rigid; (* remember what type of affixment *)
c2↑.rigid := spc↑.rigid;
b := ev <> nil; (* trans by var given? *)
c1↑.tvarp := b;
c2↑.tvarp := b;
if b then
begin (* indirect trans pointer *)
upTrans(ev↑.t,t); (* store away relation trans *)
c1↑.tvar := ev; (* and pointers to trans var *)
c2↑.tvar := ev;
end
else
begin (* direct trans *)
upTrans(c1↑.tval,t); (* store away relation trans *)
upTrans(c2↑.tval,t);
end;
b := false; (* assume no conflict *)
if not f1↑.ftype then (* f1 is a device *)
if not f2↑.ftype then b := f1 <> f2 (* f2 is also a device! *)
else
if f2↑.dev <> nil then b := f2↑.dev <> f1 (* f2 already dynamic *)
else affixaux(f2,f1,1) (* f2 now dynamic *)
else (* f1 is a frame *)
if not f2↑.ftype then (* f2 is a device *)
if f1↑.dev <> nil then b := f1↑.dev <> f2 (* f1 already dynamic *)
else affixaux(f1,f2,1) (* f1 now dynamic *)
else (* both frames *)
if f1↑.dev <> nil then (* f1 is dynamic *)
if f2↑.dev <> nil then b := f1↑.dev <> f2↑.dev (* both dynamic *)
else affixaux(f2,f1↑.dev,f1↑.dcntr+1) (* f2 now dynamic *)
else
if f2↑.dev <> nil then affixaux(f1,f2↑.dev,f2↑.dcntr+1); (* f1 now dynamic *)
if b then
begin
pp20L('Can''t have an affixm',20); pp20('ent chain connecting',20);
pp20(' two devices togethe',20); pp5('r! ',2); ppLine;
end;
mode := 0;
spc := spc↑.next;
end;
end;
procedure doUnfix;
var f1, f2: framep; ev: enventryp;
begin
with curInt↑ do
begin (* subscripts for frame1 & frame2 on stack *)
ev := gtVarn(spc↑.frame1); (* access variable *)
f1 := ev↑.f;
ev := gtVarn(spc↑.frame2); (* access variable *)
f2 := ev↑.f;
unfix(f1,f2); (* now unfix them *)
mode := 0;
spc := spc↑.next;
end;
end;
(* aux routines for motions: forcebits, getMechbits, moveStart, moveEnd, moveRetry *)
function forcebits(fn: nodep; var negv: boolean): integer;
var vec: vectorp; fbits: integer;
begin
fbits := XFORCE;
negv := false;
vec := nil;
with fn↑.fvec↑ do
if ntype = leafnode then vec := pcval↑.v (* first check if axis vector *)
else if op = vnegop then (* or negative axis vector *)
if arg1↑.ntype = leafnode then
begin vec := arg1↑.pcval↑.v; negv := true end;
if vec = yhat then fbits := YFORCE
else if vec = zhat then fbits := ZFORCE
else if vec <> xhat then negv := false;
if fn↑.ftype >= torque then fbits := fbits + XMOMENT;
forcebits := fbits;
end;
function getMechbits: integer;
var i: integer;
begin
with curInt↑ do
if mech = nil then i := BARMDEV (* default to blue arm *)
else if mech↑.ftype then
if mech↑.dev <> nil then i := mech↑.dev↑.mech
else i := BARMDEV (* default to blue arm *)
else i := mech↑.mech;
getMechbits := i;
end;
procedure moveStart;
var cl: nodep; st: statementp;
begin (* enable all cmons *)
cl := curInt↑.spc↑.clauses;
while cl <> nil do (* run through clauses *)
begin (* check for condition monitors to enable *)
st := nil;
with cl↑ do
if ntype = cmonnode then
begin if not (cmon↑.deferCm or errHandlerp) then st := cmon end
else if ntype = viaptnode then st := vcode
else if (ntype = deprnode) or (ntype = apprnode) then st := code;
if st <> nil then
begin
with st↑.cdef↑ do
cmonEnable(getVar(level,offset)); (* enable cmon control block *)
end;
cl := cl↑.next;
end;
end;
procedure moveEnd;
var cl, val: nodep; st, err: statementp; e: enventryp; ev: eventp; fr: framep;
mechbits, errbits, angle, i: integer; errval: errortypes;
b: boolean; ch: char; kludge: interr;
begin (* disable all cmons, end of motion cleanup, error checking etc. *)
with curInt↑ do
begin
b := true;
cl := spc↑.clauses;
while cl <> nil do (* run through clauses *)
begin (* check for condition monitors to disable *)
st := nil;
with cl↑ do
if (ntype = cmonnode) and not errHandlerp then st := cmon
else if ntype = viaptnode then st := vcode
else if (ntype = deprnode) or (ntype = apprnode) then st := code;
if st <> nil then
begin
with st↑.cdef↑ do
e := getVar(level,offset); (* get cmon control block *)
if e↑.c↑.running then b := false (* is it running now? *)
else cmonDisable(e↑.c); (* if not disabled it *)
end;
cl := cl↑.next;
end;
if not b then sleep(30) (* wait for cmon's to finish *)
else
begin (* all cmon's are now done *)
if mech↑.ftype then (* get offset of device error variable *)
if mech↑.dev <> nil then i := mech↑.dev↑.vari↑.offset + 1
else i := 1 (* assume barm *)
else i := mech↑.vari↑.offset + 1;
push(newNode); (* *** for SAIL simulation version *** *)
with sp↑ do (* *** " " *** *)
begin ntype := leafnode; ltype := svaltype; s := 0.0 end; (* *** " " *** *)
errbits := round(sp↑.s); (* remember error value *)
(* Since losing Pascal doesn't have an inverse for ord *)
kludge.i := errbits div 128; (* recover error type *)
errval := kludge.err;
angle := errbits mod 128; (* also bad angles (if applicable) *)
errbits := errbits - angle; (* strip out angle info *)
setVal(0,i); (* now pop it off stack & store it away *)
err := nil;
cl := spc↑.clauses;
while cl <> nil do (* run through clauses *)
begin (* check for error checker to run *)
with cl↑ do
if (ntype = cmonnode) and errHandlerp then
begin
val := getNval(cmon↑.oncond↑.eexpr,b); (* get error bits to check *)
if errbits = round(val↑.s) then err := cmon↑.conclusion;
if b then relnode(val);
end;
cl := cl↑.next;
end;
mode := 0; (* get ready for next statement *)
if errbits <> 0 then (* was there an error? *)
if err <> nil then
begin (* run error checker *)
spc := err;
end
else
begin (* print error message *)
if mech = nil then fr := barm
else if mech↑.ftype then (* first tell what device *)
if mech↑.dev <> nil then fr := mech↑.dev
else fr := barm
else fr := mech;
with fr↑.vari↑.name↑ do prntStrng(length,name);
pp5(' - ',3);
if errval = nopower then
begin
pp20('arm interface power ',20); pp20('supply turned off ',17);
pp20L(' (check joint br',20); pp20('ake switches) ',13);
ppLine;
end
else if errval = devbusy then
begin pp20('device currently in ',20); pp5('use ',4) end
else
begin
case errval of
srvdead: pp10('servo dead',10);
adcdead: pp10('a/d error ',9);
panicb: pp20('panic button pushed ',19);
exjtfc: begin pp20('excessive force enco',20); pp10('untered ',7); end;
timout: pp10('time out ',8);
paslim: pp20('stop limit exceeded ',20);
noarmsol: begin pp20('no arm solution whil',20); pp10('e servoing',10) end;
end;
badJoints(angle); (* tell which joint(s) were bad, if any *)
end;
pp20L('"P" to proceed, "R" ',20); pp20('to retry the motion ',19);
b := (spc↑.stype <> operatetype) and (spc↑.stype <> centertype);
if b then
begin pp20(', "F" to move direct',20); pp20('ly to destination ',17) end;
pp20L(' or "B" to break to',20); pp20(' debugger: ',11);
ppOutNow;
mode := 4;
curInt↑.next := readQueue; (* *** should check that no other *)
readQueue := curInt; (* process is waiting, but... *** *)
curInt↑.status := inputqueue;
curInt := nil;
resched := true;
end
else
begin (* all ok - move on to next statement *)
spc := spc↑.next;
end
end
end;
end;
procedure moveRetry;
var ch: ascii; ev: eventp; mechbits: integer; fr: framep;
begin
with curInt↑ do
begin
mode := 0;
inputReady := false;
ch := inputLine[1]; (* what does luser want to do now? *)
if ord(ch) > 140B then ch := chr(ord(ch)-40B); (* convert to uppercase *)
if ch = 'B' then running := false (* break to debugger, proceed will retry *)
else if ch = 'P' then spc := spc↑.next (* move on to next statement *)
(* else if ch = 'R' then nothing to do *)
else if (ch = 'F') and
(spc↑.stype <> operatetype) and (spc↑.stype <> centertype) then
begin
mode := 3;
ev := getEvent; (* event to use when motion finishes *)
ev↑.count := -1;
ev↑.waitlist := curInt;
mechbits := getMechbits;
with msg↑ do
begin
cmd := movehdrcmd;
dev := mechbits;
bits := NULLINGCB + DURLBCB; (* nonulling & duration *)
evt := ev;
dur := 5.0; (* default time of 5 seconds *)
sfac := 1.0;
if mech = nil then fr := barm
else if mech↑.ftype then
if mech↑.dev <> nil then fr := mech↑.dev
else fr := barm
else fr := mech;
if spc↑.stype = movetype then
begin
n := 1; (* only one segment *)
sendCmd; (* send over move header *)
cmd := movesegcmd;
bits := DESTPTCB;
sendTrans(fr↑.tdest); (* send over destination point *)
end
else
begin
pos := fr↑.sdest;
if pos < 0.0 then
begin (* no dest specified *)
pos := 0.0;
if spc↑.stype = opentype then bits := 3 else bits := 1;
(* *** need to set DURLBCB too??? *** *)
end
else
bits := bits + DESTPTCB; (* indicate specifying opening *)
if mechbits = VISEDEV then
begin
cmd := operatecmd; (* vise uses an operate command *)
v2 := 0.0; (* no stop wait time *)
end;
sendCmd;
end;
end;
(* if mechbits <> VISEDEV then signalArm; (* start it up *)
(* curInt↑.status := devicewait; (* don't for simulation version *)
(* curInt := nil;
(* resched := true; (* swap someone else in *)
freeEvent(ev); (* sim ver *)
end;
end;
end;
procedure doCmon;
var e: enventryp; n: nodep; b: boolean; val: nodep; r: real; fbits: integer;
begin
with curInt↑ do
case mode of
1: begin
if not spc↑.deferCm then (* check it's not a deferred cmon *)
begin (* need to enable the cmon *)
with spc↑.cdef↑ do
cmonEnable(getEntry(level,offset)); (* enable cmon control block *)
end;
mode := 0;
spc := spc↑.next;
end;
2: begin (* deal with ON condition *)
n := nil;
mode := 3; (* set up for doing conclusion next time *)
if spc↑.exprCm then
begin (* test if expression is now true *)
n := pop; (* get expression value *)
if n↑.s = 0.0 then
begin
sleep(20); (* no good - try again in 0.33 seconds *)
mode := 0;
end;
end
else if spc↑.oncond↑.ntype = durnode then
begin (* duration cmon *)
n := pop;
sleep(round(n↑.s * 60)); (* get wait time (in 60Hz ticks) *)
end
else if spc↑.oncond↑.ntype = forcenode then
begin (* force sensing *)
val := getNval(spc↑.oncond↑.fval,b); (* get force magnitude *)
r := val↑.s;
if b then relNode(val);
fbits := forcebits(spc↑.oncond,b);
with spc↑.oncond↑ do
begin
if (ftype = absforce) or (ftype = abstorque) then fbits := fbits + SIGMAG;
if b then begin r := -r; if frel < seqop then fbits := fbits + SIGGE end
else if frel >= seqop then fbits := fbits + SIGGE;
end;
(* deal with which arm here *) fbits := fbits + BLUARM; (* for now *)
cm↑.fbits := fbits; (* remember bits in cmoncb *)
with msg↑ do
begin
cmd := forcesigcmd;
bits := fbits;
evt := cm↑.evt;
mag := r;
end;
sendCmd;
cm↑.evt↑.count := -1;
cm↑.evt↑.waitlist := curInt; (* put us on event waitlist *)
curInt↑.status := forcewait;
curInt := nil; (* swap in someone else *)
resched := true;
end
else if spc↑.oncond↑.ntype = departingnode then
begin (* departing cmon *)
sleep(30); (* wait 0.5 seconds (in 60Hz ticks) *)
end
else
begin (* event cmon *)
if spc↑.oncond↑.ntype = arrivalnode then
with spc↑.oncond↑.evar↑ do e := getVar(level,offset)
else e := gtVarn(spc↑.oncond);
cm↑.evt := e↑.evt; (* save pointer to event we're waiting on *)
e↑.evt↑.count := e↑.evt↑.count - 1;
if e↑.evt↑.count <= 0 then (* hasn't been signalled yet, need to wait *)
begin
addPdb(e↑.evt↑.waitlist,curInt); (* add us to wait list *)
curInt↑.status := eventqueue;
curInt := nil; (* swap in someone else *)
resched := true;
end;
end;
if n <> nil then relNode(n);
end;
3: begin
mode := 0;
if cm↑.enabled then (* check that we're still enabled *)
begin
cm↑.running := true; (* set up current cmon status *)
cm↑.enabled := false;
spc := spc↑.conclusion;
end
else
begin
curInt↑.status := nullqueue;
curInt := nil; (* we should go away *)
resched := true; (* now swap in highest priority process *)
end;
end;
end;
end;
procedure doMove;
var appr,depr,dest,arrv,wobble,sfac,dur,ffr,stiff,gather,zwrist,n: nodep;
cl,val,val1,val2: nodep; t,tl,tb: transp; st: statementp; e: enventryp;
r: real; fbits,nsegs,mechbits,i,cmForce,useForce: integer; fr: framep;
b,b1,b2,nulling,apprp,deprp: boolean; ev: eventp;
function getLoc(n: nodep): transp;
var tp: transp; b: boolean;
begin
n := getNval(n,b);
tp := n↑.t;
if b then relnode(n);
if t <> nil then tp := ttmul(t,tp);
getLoc := tp;
end;
function getDepr(n: nodep; b: boolean): transp;
var tp: transp; v: vectorp;
begin
if n↑.ltype = svaltype then tp := tmake(niltrans,svmul(n↑.s,zhat))
else if n↑.ltype = vectype then tp := tmake(niltrans,n↑.v)
else tp := n↑.t;
if b then relnode(n);
tp := ttmul(tb,tp); (* shift to proper coord sys *)
if t <> nil then tp := ttmul(t,tp);
getDepr := tp;
end;
procedure getCode(s: statementp);
var e: enventryp;
begin
if s = nil then e := nil
else
begin
with s↑ do
if stype = signaltype then e := gtVarn(event)
else e := gtVarn(oncond);
msg↑.evt := e↑.evt; (* event to signal for code *)
msg↑.bits := msg↑.bits + CODECB;
end;
end;
begin
with curInt↑ do
begin
st := spc; (* remember MOVE statement *)
case mode of
1: begin (* set up force system, enable all cmons *)
e := gtVarn(spc↑.cf); (* remember what we're moving *)
mech := e↑.f;
if mech↑.ftype then (* check it's a device *)
if mech↑.dev = nil then
begin (* yow! frame that's not affixed to an arm *)
pp20L('Control frame not af',20); pp20('fixed to any device:',20);
pp20(' Assuming barm ',14); ppLine;
end;
ffr := nil;
stiff := nil;
gather := nil;
zwrist := nil;
cmForce := 0;
useForce := 0;
cl := spc↑.clauses;
while cl <> nil do (* run through clauses *)
with cl↑ do
begin
if ntype = ffnode then ffr := cl
else if ntype = stiffnode then stiff := cl
else if ntype = gathernode then gather := cl
else if ntype = wristnode then zwrist := cl
else if ntype = forcenode then useForce := useForce + 1
else if ntype = cmonnode then
if cmon↑.oncond↑.ntype = forcenode then cmForce := cmForce + 1;
cl := next;
end;
if (ffr <> nil) or (cmForce + useForce > 0) or (gather <> nil) then
begin
msg↑.cmd := setccmd;
(* deal with which arm here *) fbits := BLUARM; (* but for now... *)
if ffr <> nil then
begin
val := getNval(ffr↑.ff,b); (* get force frame value *)
if ffr↑.csys then fbits := fbits + FTABLE;
msg↑.bits := fbits;
sendTrans(val↑.t); (* send command & trans over *)
if b then relNode(val);
end
else
begin
msg↑.bits := fbits + FTABLE;
sendTrans(niltrans); (* send command & trans over *)
end;
(* signalArm; (* wake up ARM servo background job *)
end;
if zwrist <> nil then b := zwrist↑.notp
else if (ffr <> nil) or (stiff <> nil) then b := true
else b := false;
if b then
begin
msg↑.cmd := zerowristcmd; (* tell arm servo to zero wrist *)
sendCmd;
end;
if stiff <> nil then
begin
val1 := getNval(stiff↑.fv,b1); (* get force vector *)
val2 := getNval(stiff↑.mv,b2); (* get moment vector *)
if stiff↑.coc <> nil then
begin
val := getNval(stiff↑.coc,b); (* get coc value *)
t := val↑.t;
end
else begin t := niltrans; b := false end;
with msg↑ do
begin
cmd := setstiffcmd;
for i := 1 to 3 do
begin
t[i] := val1↑.v↑.val[i];
t[i+3] := val2↑.v↑.val[i];
end;
end;
sendTrans(t); (* send stiffnesses & coc trans over *)
(* signalArm; (* wake up ARM servo background job *)
if b1 then killNode(val1);
if b2 then killNode(val2);
if b then relNode(val);
end
else if useForce > 0 then
begin (* add default stiffness *)
with msg↑ do
begin
cmd := setstiffcmd;
for i := 1 to 3 do
begin
t[i] := 40;
t[i+3] := 100;
end;
end;
sendTrans(niltrans); (* send stiffnesses & coc trans over *)
(* signalArm; (* wake up ARM servo background job *)
end;
if gather <> nil then
begin
(* deal with which arm here someday *)
with msg↑ do
begin
cmd := gathercmd;
bits := gather↑.gbits;
end;
sendCmd; (* send gather command over *)
end;
if ffr <> nil then (* no bias forces if no force frame *)
begin
cl := spc↑.clauses;
while cl <> nil do (* run through clauses *)
begin
with cl↑ do
if ntype = forcenode then (* check for bias forces *)
begin
val := getNval(cl↑.fval,b); (* get force magnitude *)
r := val↑.s;
if b then relnode(val);
fbits := forcebits(cl,b);
if b then r := -r;
(* deal with which arm here *) fbits := fbits + BLUARM; (* but for now... *)
with msg↑ do
begin
cmd := biasoncmd;
bits := fbits;
mag := r;
end;
sendCmd; (* tell arm about bias force *)
end;
cl := cl↑.next;
end;
end;
moveStart; (* enable all condition monitors for move *)
mode := 2;
end;
2: begin (* set up motion specs for arm code & send it over *)
ev := getEvent; (* event to use for signalling when motion finishes *)
ev↑.count := -1;
ev↑.waitlist := curInt;
mechbits := getMechbits;
nsegs := 0;
if mech↑.ftype then
if mech↑.dev <> nil then fr := mech↑.dev (* get frame for device *)
else fr := barm
else fr := mech;
nulling := true; (* no nulling is the default *)
dest := nil;
wobble := nil;
sfac := nil;
dur := nil;
arrv := nil;
appr := nil;
depr := nil;
apprp := true; (* assume default approach *)
deprp := fr↑.depr <> nil; (* default departure if last had approach *)
cl := spc↑.clauses;
while cl <> nil do (* run through clauses *)
with cl↑ do
begin
if ntype = destnode then begin dest := cl; nsegs := nsegs + 1 end
else if ntype = wobblenode then wobble := cl
else if ntype = sfacnode then sfac := cl
else if ntype = durnode then dur := cl
else if ntype = nullingnode then nulling := notp
else if ntype = apprnode then
begin
appr := cl;
if loc = nil then apprp := false (* approach = nildeproach *)
else begin apprp := true; nsegs := nsegs + 1 end
end
else if ntype = deprnode then
begin
depr := cl;
if loc = nil then deprp := false (* departure = nildeproach *)
else begin deprp := true; nsegs := nsegs + 1 end
end
else if ntype = viaptnode then nsegs := nsegs + 1
else if ntype = cmonnode then
begin
if cmon↑.oncond↑.ntype = arrivalnode then arrv := cmon↑.oncond;
end;
cl := next;
end;
if deprp or mech↑.ftype then
tb := feval(mech); (* get current cf position *)
if deprp then
if depr <> nil then (* explicit departure point? *)
tb↑.refcnt := tb↑.refcnt + 1 (* need it to compute departure *)
else nsegs := nsegs + 1; (* add in default departure seg *)
if apprp and (appr = nil) then (* default approach point? *)
with dest↑.loc↑ do
if ((ntype = leafnode) and (ltype = varitype)) or
((ntype = exprnode) and (op = arefop)) then
nsegs := nsegs + 1 (* add in default approach seg *)
else apprp := false; (* don't want default approach *)
if mech↑.ftype then
begin (* get offset trans to take cf to arm *)
t := whereArm(mechbits); (* Get current device pos *)
t := ttmul(t,tinvrt(tb)); (* compute offset *)
end
else t := nil; (* no offset needed *)
with msg↑ do
begin
cmd := movehdrcmd;
dev := mechbits;
if nulling then bits := NULLINGCB else bits := 0;
n := nsegs;
evt := ev;
end;
if sfac <> nil then
begin (* use local speed factor *)
val := getNval(sfac↑.clval,b);
msg↑.sfac := val↑.s;
if b then relnode(val);
end
else
begin (* use global speed factor *)
msg↑.sfac := speedfactor↑.s;
end;
if dur <> nil then (* duration *)
begin
val := getNval(dur↑.durval,b);
msg↑.dur := val↑.s;
if dur↑.durrel < seqop then i := DURLBCB
else if dur↑.durrel > seqop then i := DURUBCB
else i := DUREQCB;
msg↑.bits := msg↑.bits + i;
if b then relnode(val);
end;
if wobble <> nil then (* wobble *)
begin
val := getNval(wobble↑.clval,b);
msg↑.wobble := val↑.s;
msg↑.bits := msg↑.bits + WOBBLECB;
if b then relnode(val);
end;
sendCmd; (* tell arm servo we're starting a motion *)
msg↑.cmd := movesegcmd; (* now get values for trajectory points *)
if deprp then (* departure: loc & event *)
begin
msg↑.bits := DEPRPTCB;
if depr = nil then tl := fr↑.depr (* default departure point *)
else
begin (* explicit departure point *)
n := getNval(depr↑.loc,b);
tl := getDepr(n,b);
tb↑.refcnt := tb↑.refcnt - 1;
if tb↑.refcnt <= 0 then relTrans(tb); (* done with it now *)
getCode(depr↑.code);
end;
sendTrans(tl);
end;
cl := spc↑.clauses;
while cl <> nil do (* run through clauses *)
begin
with cl↑ do
if ntype = viaptnode then (* vias: loc, duration, velocity & event *)
begin
msg↑.bits := VIAPTCB;
tl := getLoc(via);
if duration <> nil then
begin
val := getNval(duration↑.durval,b);
msg↑.dur := val↑.s;
if duration↑.durrel < seqop then i := DURLBCB
else if duration↑.durrel > seqop then i := DURUBCB
else i := DUREQCB;
msg↑.bits := msg↑.bits + i;
if b then relnode(val);
end;
if velocity <> nil then
begin
val := getNval(velocity,b);
msg↑.bits := msg↑.bits + VELOCCB;
with val↑.v↑ do
begin
msg↑.v1 := val[1];
msg↑.v2 := val[2];
msg↑.v3 := val[3];
end;
if b then relnode(val);
end;
getCode(cl↑.vcode);
sendTrans(tl);
end;
cl := cl↑.next;
end;
if apprp then (* approach: loc & event *)
begin
msg↑.bits := APPRPTCB;
if appr <> nil then
begin (* explicit approach point *)
n := getNval(appr↑.loc,b);
getCode(appr↑.code);
end;
tb := getLoc(dest↑.loc); (* need to get destination location *)
tb↑.refcnt := tb↑.refcnt + 1; (* make sure we keep it for later *)
if appr <> nil then tl := getDepr(n,b) (* explicit approach point *)
else
begin (* default appoach point *)
tl := tvadd(tb,svmul(3,zhat));
if t <> nil then tl := ttmul(t,tl);
end;
tb↑.refcnt := tb↑.refcnt - 1;
upTrans(fr↑.appr,tl); (* save it for next motion *)
sendTrans(tl);
end
else
begin
tb := getLoc(dest↑.loc); (* get destination for below *)
upTrans(fr↑.appr,nil); (* remember no default depr for next motion *)
end;
(* destination: loc & event *)
uptrans(fr↑.tdest,tb); (* make a copy of dest for later use *)
msg↑.bits := DESTPTCB;
if arrv <> nil then
begin
with arrv↑.evar↑ do e := getVar(level,offset);
msg↑.evt := e↑.evt; (* event to signal for code *)
msg↑.bits := msg↑.bits + CODECB;
end;
sendTrans(tb);
(* signalArm; (* finally let background job deal with traj *)
mode := 3;
(* curInt↑.status := devicewait; (* don't for simulation version *)
(* curInt := nil;
(* resched := true; (* swap someone else in *)
freeEvent(ev); (* sim ver *)
end;
3: moveEnd; (* do end of motion cleanup, run error handler, etc. *)
4: moveRetry; (* deal with user response if there was an error *)
end;
if curInt <> nil then (* in case we're waiting for an error response *)
if spc = st↑.next then
begin (* doesn't appear to have been any errors *)
if mech↑.ftype then (* get frame for device *)
if mech↑.dev <> nil then fr := mech↑.dev
else fr := barm
else fr := mech;
upTrans(fr↑.depr,fr↑.appr); (* update default departure point *)
end;
end;
end;
procedure doOperate;
var durcl,vel,torquecl,cl,v: nodep; e: enventryp; b,ccw: boolean; ev: eventp;
begin (* deal with driver *)
with curInt↑ do
case mode of
1: begin
e := gtVarn(spc↑.cf); (* remember what we're moving *)
mech := e↑.f;
moveStart; (* enable all condition monitors for move *)
mode := 2;
end;
2: begin (* set up motion specs for arm code & send it over *)
ev := getEvent; (* event to use for signalling when motion finishes *)
ev↑.count := -1;
ev↑.waitlist := curInt;
durcl := nil;
vel := nil;
torquecl := nil;
ccw := false;
cl := spc↑.clauses;
while cl <> nil do (* run through clauses *)
with cl↑ do
begin
if ntype = durnode then durcl := cl
else if ntype = forcenode then
begin
if ftype = torque then torquecl := cl
else if ftype = angvelocity then vel := cl
end
else if ntype = cwnode then ccw := notp;
cl := next;
end;
with msg↑ do
begin
cmd := operatecmd;
dev := getMechbits;
bits := 0;
evt := ev;
dur := 5.0; (* default values *)
v1 := 60.0; (* angular velocity *)
v2 := 0.0; (* torque *)
if durcl <> nil then
begin
v := getNval(durcl↑.durval,b); (* get duration value *)
dur := v↑.s;
if b then relNode(v);
end;
if vel <> nil then
begin
v := getNval(vel↑.fval,b); (* get angular velocity value *)
v1 := v↑.s;
if b then relNode(v);
end;
if torquecl <> nil then
begin
v := getNval(torquecl↑.fval,b); (* get torque value *)
v2 := v↑.s;
if b then relNode(v);
end;
if ccw then
begin (* turning counterclockwise *)
v1 := - v1;
v2 := - v2;
end;
end;
sendCmd; (* pass info to ARM servo *)
mode := 3;
(* curInt↑.status := devicewait; (* don't for simulation version *)
(* curInt := nil;
(* resched := true; (* swap someone else in *)
freeEvent(ev); (* sim ver *)
end;
3: moveEnd; (* do end of motion cleanup, run error handler, etc. *)
4: moveRetry; (* deal with user response if there was an error *)
end;
end;
procedure doOpen; (* & doClose *)
var dest,sfac,durcl,swt,cl,v: nodep; e: enventryp; ev: eventp;
opening,dtime,sf,swtime: real; mechbits: integer; b,nulling: boolean;
begin
with curInt↑ do
case mode of
1: begin
e := gtVarn(spc↑.cf); (* remember what we're moving *)
mech := e↑.f;
moveStart; (* enable all condition monitors for move *)
mode := 2;
end;
2: begin (* set up motion specs for arm code & send it over *)
ev := getEvent; (* event to use for signalling when motion finishes *)
ev↑.count := -1;
ev↑.waitlist := curInt;
mechbits := getMechbits;
(* run through clauses for dest, duration & speed factor/stop wait time specs *)
dest := nil;
durcl := nil;
sfac := nil;
swt := nil;
nulling := true; (* nonulling is the default *)
cl := spc↑.clauses;
while cl <> nil do (* run through clauses *)
with cl↑ do
begin
if ntype = destnode then dest := cl
else if ntype = durnode then durcl := cl
else if ntype = sfacnode then sfac := cl
else if ntype = swtnode then swt := cl
else if ntype = nullingnode then nulling := notp;
cl := next;
end;
if sfac = nil then sf := speedfactor↑.s (* use global speed factor *)
else
begin
v := getNval(sfac↑.clval,b); (* get local speed factor value *)
sf := v↑.s;
if b then relNode(v);
end;
if durcl = nil then dtime := 0
else
begin
v := getNval(durcl↑.durval,b); (* get duration value *)
dtime := v↑.s;
if b then relNode(v);
end;
if swt = nil then swtime := 0
else
begin
v := getNval(swt↑.clval,b); (* get stop wait time value *)
swtime := v↑.s;
if b then relNode(v);
end;
if dest = nil then
begin
opening := 0;
mech↑.sdest := -1; (* so we know there was no dest *)
end
else
begin
v := getNval(dest↑.loc,b); (* get opening value *)
opening := v↑.s;
mech↑.sdest := opening; (* remember it *)
if b then relNode(v);
end;
with msg↑ do
begin
dev := mechbits;
evt := ev;
if nulling then bits := NULLINGCB else bits := 0;
if dest = nil then
begin
pos := 0.0;
if spc↑.stype = opentype then bits := 3 else bits := 1;
end
else
begin
pos := opening;
bits := bits + DESTPTCB; (* indicate we're specifying opening *)
end;
if durcl = nil then dur := 0.0
else
begin
dur := dtime;
bits := bits + DUREQCB;
end;
sfac := sf;
if mechbits = VISEDEV then
begin
cmd := operatecmd; (* vise uses an operate command *)
if swt = nil then
if dest = nil then v2 := 0.25 else v2 := 0.0 (* default values *)
else v2 := swtime;
if durcl = nil then dur := 8.0;
sendCmd;
end
else
begin
cmd := movehdrcmd; (* deal with hand *)
sendCmd;
(* signalArm; (* since movehdr normally followed by movesegs *)
end;
end;
mode := 3;
(* curInt↑.status := devicewait; (* don't for simulation version *)
(* curInt := nil;
(* resched := true; (* swap someone else in *)
freeEvent(ev); (* sim ver *)
end;
3: moveEnd; (* do end of motion cleanup, run error handler, etc. *)
4: moveRetry; (* deal with user response if there was an error *)
end;
end;
procedure doCenter;
var e: enventryp; ev: eventp;
begin
with curInt↑ do
case mode of
1: begin
e := gtVarn(spc↑.cf); (* remember what we're moving *)
mech := e↑.f;
moveStart; (* enable all condition monitors for move *)
mode := 2;
end;
2: begin (* set up motion specs for arm code & send it over *)
ev := getEvent; (* event to use for signalling when motion finishes *)
ev↑.count := -1;
ev↑.waitlist := curInt;
with msg↑ do
begin
cmd := centercmd;
dev := getMechbits;
bits := 0;
evt := ev;
end;
sendCmd; (* initiate the center operation *)
mode := 3;
(* curInt↑.status := devicewait; (* don't for simulation version *)
(* curInt := nil;
(* resched := true; (* swap someone else in *)
freeEvent(ev); (* sim ver *)
end;
3: moveEnd; (* do end of motion cleanup, run error handler, etc. *)
4: moveRetry; (* deal with user response if there was an error *)
end;
end;
procedure doStop;
var mechbits: integer; e: enventryp;
begin
with curInt↑ do
begin
if spc↑.cf = nil then mechbits := getMechbits (* use current mech *)
else
begin
e := gtVarn(spc↑.cf); (* see what we're stopping *)
with e↑.f↑ do
if ftype then
if dev <> nil then mechbits := dev↑.mech
else
begin (* yow! frame that's not affixed to a device *)
pp20L('Attempt to stop fram',20); pp20('e not affixed to any',20);
pp20(' device: Assuming ba',20); pp5('rm ',2); ppLine;
mechbits := BARMDEV;
end
else mechbits := mech;
end;
with msg↑ do
begin
cmd := stopcmd;
dev := mechbits;
end;
sendCmd; (* tell arm servo to stop device *)
mode := 0;
spc := spc↑.next;
end;
end;
procedure doRetry;
var b: boolean;
begin
with curInt↑ do
begin
if spc↑.rparent <> nil then
begin
b := true;
while b and (spc↑.olevel < getELev(env)) do
begin (* make sure all cmon's in outer environments have finished *)
b := cmonCheck;
if b then killEnv; (* flush all environments out to move *)
end;
if b then (* no cmons now running *)
begin
(* *** might need to clean up stack some here (fornodes) *** *)
spc := spc↑.rcode; (* go redo the previous motion *)
mode := 0;
end
else sleep(30); (* give cmons time to finish *)
end
else
begin
spc := spc↑.next; (* just go on to next statement *)
mode := 0;
end;
end;
end;
procedure doSetbase;
begin
with curInt↑ do
begin
msg↑.cmd := zerowristcmd; (* tell ARM servo to zero wrist *)
sendCmd;
mode := 0;
spc := spc↑.next;
end;
end;
procedure doWrist;
var fv,tv: enventryp; v: vectorp; i: integer;
begin
with curInt↑ do
begin
fv := gtVarn(spc↑.fvec); (* get where to store results *)
tv := gtVarn(spc↑.tvec);
if fv↑.v <> nil then (* flush any old values *)
with fv↑.v↑ do
begin
refcnt := refcnt - 1;
if refcnt <= 0 then relVector(fv↑.v);
end;
if tv↑.v <> nil then
with tv↑.v↑ do
begin
refcnt := refcnt - 1;
if refcnt <= 0 then relVector(tv↑.v);
end;
msg↑.cmd := wristcmd;
getReply; (* have ARM servo read wrist *)
v := newVector;
for i := 1 to 3 do v↑.val[i] := msg↑.t[i];
fv↑.v := v; (* store away force vector *)
v↑.refcnt := 1;
v := newVector;
for i := 1 to 3 do v↑.val[i] := msg↑.t[i+3];
tv↑.v := v; (* store away torque vector *)
v↑.refcnt := 1;
mode := 0;
spc := spc↑.next;
end;
end;
(* command loop *)
procedure interp(dLev: integer);
var p,pp: pdbp; n: nodep; b,breakNow: boolean; ch: ascii; minPriority: integer;
begin
debugLevel := dLev;
minPriority := 10 * debugLevel;
if curInt <> nil then curInt↑.status := nowrunning;
running := true;
resched := false;
breakNow := false;
escapeI := false;
inputp := 0;
inputReady := false;
if readQueue <> nil then
if readQueue↑.priority >= minPriority then (* must be at current level *)
with readQueue↑ do
begin (* remind user we're waiting for input *)
b := true;
if epc <> nil then
begin
b := false;
if epc↑.op = queryop then pp20L('Type Y or N: ',13)
else if epc↑.op = inscalarop then pp20L('Scalar please: ',15)
else b := true;
end;
if b then
begin
b := false;
if (spc↑.stype = prompttype) or (spc↑.stype = waittype) then
pp20L('Type P to proceed: ',19)
else if (movetype <= spc↑.stype) and (spc↑.stype <= centertype) then
begin
pp20L('"P" to proceed, "R" ',20); pp20('to retry the motion ',19);
if (spc↑.stype <> operatetype) and (spc↑.stype <> centertype) then
begin pp20(', "F" to move direct',20);
pp20('ly to destination ',17) end;
pp20L(' or B to break to d',20); pp10('ebugger: ',9);
end
else b := true;
end;
if not b then ppOutNow;
(* *** else ??? flush readQueue ??? *** *)
end;
while running do
begin
if msgp then (* any messages pending? *)
repeat (* yup - go read them *)
msgp := false; (* reset flag *)
b := getArm; (* read next message *)
if b then msgDispatch (* if we actually got one then deal with it *)
until not b; (* keep going til no more messages to read *)
if stime <> 0 then (* hack on 10 to simulate time *)
begin
stime := stime - 1;
if stime = 0 then (* time to wake up sleeping processes *)
begin
n := clkQueue; (* get waitlist node *)
clkQueue := n↑.next;
if clkQueue <> nil then stime := clkQueue↑.when; (* set stime for next *)
p := n↑.who;
while p <> nil do (* add waiting processes to activeInts list *)
begin
pp := p↑.next; (* remember where we are in list *)
p↑.status := runqueue;
addPdb(activeInts,p);
p := pp;
end;
relNode(n);
if curInt = nil then resched := true
else if activeInts↑.priority > curInt↑.priority then resched := true;
end;
end;
if resched then (* schedule highest priority process *)
begin
resched := false;
if curInt <> nil then
begin
curInt↑.status := runqueue;
addPdb(activeInts,curInt);
end;
curInt := activeInts; (* now swap in highest priority process *)
if activeInts <> nil then
begin
activeInts := activeInts↑.next;
curInt↑.next := nil;
curInt↑.status := nowrunning;
with curInt↑ do
breakNow := (mode = 0) and spc↑.bpt;
end;
end;
if readQueue <> nil then (* is some process waiting for terminal input? *)
if readQueue↑.priority >= minPriority then (* must be at current level *)
while anyChar(ch) and (not inputReady) do
begin
if ch = chr(15B) then
begin (* process the line now *)
ppLine; (* echo it *)
inputReady := true;
if inputp = 0 then inputLine[1] := ' '; (* for empty lines *)
if curInt <> nil then
begin
curInt↑.status := runqueue;
curInt↑.next := activeInts;
activeInts := curInt;
resched := curInt↑.priority > readQueue↑.priority; (* for next time *)
end;
curInt := readQueue; (* swap input process in now *)
curInt↑.status := nowrunning;
readQueue := curInt↑.next; (* might be a lower level joker in queue *)
curInt↑.next := nil;
breakNow := false;
end
else if (ord(ch) = 10B) or (ord(ch) = 177B) then (* backspace/delete *)
begin
if inputp > 0 then
begin (* something to delete *)
inputLine[inputp] := ' ';
inputp := inputp - 1;
ppDelChar; (* erase last character *)
end
end
else if ch <> chr(12B) then (* ignore linefeeds *)
begin
inputp := inputp + 1; (* *** should check for array overflow *** *)
inputLine[inputp] := ch;
ppChar(ch); ppOutNow; (* echo it *)
end
end;
if (curInt <> nil) and (not breakNow) then (* something to do now *)
with curInt↑ do
if priority >= minPriority then (* must be at current level *)
if epc <> nil then evalExp (* continue evaluating current expression *)
else if curInt↑.mode = 0 then
begin (* evaluate any expressions needed by current statement *)
epc := spc↑.exprs;
mode := 1;
if spc↑.stype = untiltype then epc := nil (* evaluate condition later *)
else if spc↑.stype = cmtype then (* treat enabling a cmon specially *)
if cm = nil then epc := nil
else if cm↑.cmon <> spc then epc := nil
else mode := 2; (* we're doing the ON cond *)
end
else case spc↑.stype of (* interpret the current statement *)
progtype: doProg;
blocktype: doBlock;
coblocktype: doCoblock;
coendtype,
endtype: doEnd;
fortype: doFor;
iftype: doIf;
whiletype: doWhile;
untiltype: doUntil;
casetype: doCase;
calltype: doCall;
returntype: doReturn;
printtype: doPrint;
prompttype: doPrompt;
pausetype: doPause;
aborttype: doAbort;
assigntype: doAssign;
signaltype: doSignal;
waittype: doWait;
enabletype: doEnable;
disabletype: doDisable;
cmtype: doCmon;
affixtype: doAffix;
unfixtype: doUnfix;
movetype: doMove;
operatetype: doOperate;
opentype,
closetype: doOpen; (* someday close may be different ... *)
centertype: doCenter;
stoptype: doStop;
retrytype: doRetry;
setbasetype: doSetbase;
wristtype: doWrist;
commenttype,
emptytype,
requiretype,
definetype,
declaretype,
dimdeftype: begin mode := 0; spc := spc↑.next; end; (* nothing to do *)
(* more??? *)
end;
if (curInt <> nil) and running then (* check if we've hit a breakpoint *)
with curInt↑ do
if priority >= minPriority then (* must be at current level *)
running := not((mode = 0) and spc↑.bpt);
if escapeI then
begin
b := running;
if curInt = nil then running := false
else with curInt↑ do
if priority < minPriority then running := false
else if curInt↑.mode = 0 then (* ready to start some real stmnt? *)
if (spc↑.stype <> endtype) and (spc↑.stype <> coendtype) then
running := false;
if b and not running then pp20L('Escape-I interrupt ',18);
end;
end; (* repeat til done running *)
(* finish up - leave things in a clean state *)
end;
begin
end.